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

目录

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 <TinyGsmClient.h>
#include <PubSubClient.h>
#include <Servo.h>

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. 安全与高可用实践

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

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

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

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

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


10. 结语

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

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


延伸阅读

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

  • 《MQTT Essentials》— HiveMQ Blog

  • Designing Data-Intensive Applications — Martin Kleppmann

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吉檀迦俐

你的鼓励奖是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值