使用ESP32构建安全的仓库门控系统
DoorCTL 是一款专为纽约布鲁克林一家商业洗衣设施的实际生产需求而设计的仓库装卸门控制系统。它取代了传统的实体墙面按钮,采用网页界面,所有操作均经过身份验证、按角色授权,并全程记录。
该项目特意按照生产级标准构建,而非作为原型,而是作为员工日常使用的完整系统。这意味着与典型的“ESP32 + 继电器 + 按钮”搭建不同,它在数据库层面增加了JWT身份验证、WebAuthn生物识别、基于角色的访问控制(RBAC)以及行级安全机制。
建筑
系统分为四个层次:
•PWA(渐进式网络应用)——基于Vanilla JavaScript,托管在Netlify上。用户可通过密码或生物识别登录,查看实时门禁状态,并可浏览活动记录。
•Supabase — Postgres 数据库结合 Edge Functions(Deno)。所有身份验证和授权操作都集中在此:JWT 的签发与验证、WebAuthn 验证,以及每次请求时的角色检查。
•Blynk Cloud 仅用作命令中转桥(虚拟引脚 V1/V2/V3)。在 Blynk 层不进行用户身份识别,该责任完全由 PWA 和 Supabase 承担。
•ESP32 固件——接收来自 Blynk 的指令,驱动一个双通道继电器(高电平激活,GPIO26 = 开启,GPIO27 = 关闭),并读取一个 433 MHz 高频遥控器(QIACHIP RX480E)和一个物理停止按钮。
硬件
组件:
•ESP32开发套件 — 主控制器
•双通道继电器模块(SRD-05VDC-SL-C,主动高电平)——模拟门控器上的按钮按下操作
•QIACHIP RX480E — 用于物理远程的433 MHz射频接收器
•物理停止按钮 — GPIO25 + GND 上的干接点
关键设计决定——主动高电平:在ESP32启动时,GPIO26/27默认为低电平,因此继电器在开机时保持关闭状态。这可避免ESP32重启时误触发门禁。
电隔离:继电器触点(COM/NO)与ESP32的逻辑电路在电气上是隔离的——线圈与触点之间的唯一连接是磁性。这一点很重要:门控器的电路永远不会到达ESP32的3.3V GPIO引脚。
安全架构
这是项目中超出硬件部分最远的部分。
JWT(HMAC-SHA256):登录时使用 Web Crypto API 生成令牌(无需外部库),存储在 localStorage 中,并通过 X-Doorctl-Token 头部附加到每个管理员请求中。
WebAuthn生物识别:使用@simplewebauthn/server实现真正的挑战-应答认证。私钥永远不会离开设备的安全隔离区域,服务器仅验证签名。
RBAC — 三层权限(管理员/操作员/只读):核心原则是角色永远不会被嵌入到JWT中。
每次管理员请求时,角色都会从数据库中实时读取。如果管理员将某人降级,变更会立即生效,无需等待30天的令牌过期。
审计日志:每项门禁操作——无论是来自应用程序、射频遥控器还是实体按钮——都会被记录在日志中,包含触发该操作的用户、来源以及时间戳。
行级安全:在日志表和用户表上以Postgres级别启用——这是一种独立于边缘函数逻辑的额外防护措施。
挑战与经验教训
Blynk 无法识别用户。最初尝试通过固件中的 V-pin 处理器来识别用户,但结果并不正确:Blynk 只是一个命令中转通道,所有用户归属信息都必须在 PWA/Supabase 层进行处理。
循环阻塞确实存在权衡取舍。Supabase 的 HTTP 调用(300–800 毫秒)会占用 ESP32 单核的处理能力,这意味着如果某个动作在一秒左右发生,理论上可能会错过 433 MHz 或 STOP 脉冲信号。继电器控制不会受到影响,只有日志记录可能出问题。使用异步或双核 FreeRTOS 可以解决此问题,但代价是显著增加的复杂性,因此目前这种设计是一种有意识的权衡选择。
并行性需要 FreeRTOS。在 loop() 之外单独定义一个 void 函数并不会实现并行性——它仍然会从 loop 中调用,并以完全相同的方式被阻塞。
硬件隔离并非可选。即使共享地线看似显而易见,将ESP32引脚直接并联在现有控制电路中仍存在危险,必须使用光电耦合器或继电器干触点等设备。
本文编译自hackster.io





