共享单车扫码解锁全流程超详解

目录

1. 背景与技术挑战

2. 整体架构鸟瞰

3. Step-by-Step 流程深拆

3.1 扫码阶段

3.2 鉴权阶段

3.3 指令下发 & 通信策略

3.4 解锁执行

3.5 状态回传

4. 二维码信息设计

4.1 数据字段

4.2 签名算法示例(Python)

5. 两种通信模式对比

6. 后端核心接口示例(Node.js + MQTT)

6.1 解锁请求

6.2 MQTT 监听

7. 智能锁固件示例片段(Arduino-C)

8. 客户端 BLE 解锁示例(Flutter)

9. 安全与高可用实践

10. 结语

延伸阅读

1. 背景与技术挑战

共享单车的“扫码即走”表面上只需几秒,背后却要在 前端 ⇄ 后端 ⇄ 物联网设备 三点之间完成 身份校验、指令加密、实时通信、状态回写 等一系列链路。 系统需同时满足:

维度典型指标⏱ 时延< 3 s 完成解锁🔐 安全防伪二维码、HTTPS、指令签名、硬件可信根⚖️ 并发高峰期每秒数万次扫码请求🔋 低功耗智能锁待机 90 天+

2. 整体架构鸟瞰

┌──────────┐ HTTPS ┌─────────────┐ MQTT/BLE ┌────────┐

│ App │ ─────────────▼────────────▶ │ 网关/业务层 │ ─────────────▼────────────▶ │ 智能锁 │

└──────────┘ <──JSON───▲───────────────┘ (账户&车辆) └───────────▲───────────────┘

▲ │ │

└────本地蓝牙────┘ └──状态回传───▶ 数据库

App:二维码扫描、UI 反馈、蓝牙通道(可选)

网关/业务层:统一鉴权、风控、指令下发、MQTT Broker

智能锁:蜂窝 / BLE / NB-IoT,多协议支持,控制舵机解锁

3. Step-by-Step 流程深拆

3.1 扫码阶段

sequenceDiagram

User->>App: 扫描二维码

App->>App: 解析 QR 数据 (bike_id, signature…)

容错点:二维码污损时,App 可回退到输入车牌号。

3.2 鉴权阶段

App->>Server: POST /api/v1/unlock

Server->>Redis: 查询用户余额 & 信用

Server->>DB: 检查车辆状态

alt 通过

Server-->>App: 200 OK + unlockToken

else 拒绝

Server-->>App: 403 Forbidden

end

3.3 指令下发 & 通信策略

策略 1:蜂窝直连

Server-->>MQTT Broker: PUBLISH /lock/123456/cmd {open:true}

MQTT Broker-->>BikeLock: Message

策略 2:蓝牙中转

Server-->>App: unlockToken & signedCmd

App-->>BikeLock: BLE writeCharacteristic(openCmd)

3.4 解锁执行

MCU 驱动电机旋转 720 ms → 卡榫滑动 → 机械锁体打开。

完成后把 status=opened 上报。

3.5 状态回传

BikeLock-->>Server: MQTT /lock/123456/status {opened: true, ts: 1719202020}

Server-->>App: WebSocket push "解锁成功"

4. 二维码信息设计

4.1 数据字段

{

"bike_id": "B123456",

"platform": "mobike",

"api": "https://api.mobike.com/unlock",

"ts": 1719201471,

"type": "e-bike",

"sig": "E4821B47..."

}

4.2 签名算法示例(Python)

import hmac, hashlib, base64, time, json

SECRET = b"QR_SIGN_SECRET"

payload = {

"bike_id": "B123456",

"ts": int(time.time())

}

msg = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode()

sig = base64.urlsafe_b64encode(hmac.new(SECRET, msg, hashlib.sha256).digest()).decode()

payload["sig"] = sig

print(payload)

5. 两种通信模式对比

🛰️ 蜂窝直连🔵 蓝牙中转用户距离远程可解锁必须贴近车辆设备成本⭐⭐⭐(SIM+射频)⭐(BLE 芯片)信号依赖基站覆盖手机 + BLE典型场景一线城市、电单车停车棚、信号盲区

6. 后端核心接口示例(Node.js + MQTT)

6.1 解锁请求

// unlock.controller.js

export async function unlock(req, res) {

const { bikeId } = req.body;

const userId = req.user.id;

// 1. 鉴权

if (!await checkBalance(userId)) return res.status(402).json({ msg: '余额不足' });

// 2. 车辆状态

const bike = await Bike.findById(bikeId);

if (bike.status !== 'idle') return res.status(423).json({ msg: '车辆已被占用' });

// 3. 指令下发 (蜂窝模式示例)

const cmd = { open: true, nonce: Date.now() };

mqttClient.publish(`/lock/${bikeId}/cmd`, JSON.stringify(cmd));

// 4. 记录日志

await RideLog.create({ userId, bikeId, action: 'unlock', cmd });

res.json({ code: 0, msg: '指令已发送' });

}

6.2 MQTT 监听

mqttClient.on('message', async (topic, payload) => {

const [ , bikeId, type ] = topic.split('/'); // /lock/123456/status

if (type === 'status') {

const status = JSON.parse(payload);

await Bike.updateOne({ _id: bikeId }, { status: status.opened ? 'in_use' : 'idle' });

io.to(userSocketMap[bikeId]).emit('unlock-result', status);

}

});

7. 智能锁固件示例片段(Arduino-C)

#include

#include

#include

TinyGsm modem(Serial1);

PubSubClient mqtt(modem);

Servo lockServo;

void callback(char* topic, byte* payload, unsigned int len) {

if (strcmp(topic, "/lock/123456/cmd") == 0) {

if (payload[0] == '1') { // open

lockServo.write(180);

delay(700);

lockServo.write(0);

mqtt.publish("/lock/123456/status", "{\"opened\":true}");

}

}

}

void setup() {

modem.restart();

mqtt.setServer("broker.xxx.com", 1883);

mqtt.setCallback(callback);

lockServo.attach(5);

}

8. 客户端 BLE 解锁示例(Flutter)

// 使用 flutter_blue_plus

await flutterBlue.startScan(withServices: [guidLockService]);

flutterBlue.scanResults.listen((results) async {

final r = results.firstWhere((r) => r.device.id.id == targetMac);

await r.device.connect();

var service = await r.device.discoverServices()

.then((s) => s.firstWhere((s) => s.uuid == guidLockService));

var char = service.characteristics

.firstWhere((c) => c.uuid == guidLockCharacteristic);

await char.write(utf8.encode(unlockToken)); // 写入解锁指令

});

9. 安全与高可用实践

二维码加密 + 动态盐:二维码被拍照后可被伪造,务必加入过期时间戳和 HMAC。

双向 TLS:设备与 MQTT Broker 建立 mTLS,防止中间人。

重放攻击防护:指令加入 nonce 与服务器侧 Redis-setNX。

灰度发布:智能锁 OTA 分批推送,避免全部离线。

多活架构:北京、上海双 IDC + Auto-Route,99.95% SLA。

10. 结语

本文从 二维码设计 → 后端鉴权 → 指令下发 → 设备执行 全链路拆解共享单车扫码解锁技术细节,并提供了 Node.js、Arduino、Flutter 等多端代码示例。希望对正在研发 IoT + 共享出行项目的你有所启发。

🚀 如果文章对你有帮助,欢迎点赞 👍、收藏 ⭐、评论交流!

延伸阅读

《物联网设备安全白皮书》— 360 攻防实验室

《MQTT Essentials》— HiveMQ Blog

Designing Data-Intensive Applications — Martin Kleppmann