12.MD5签名机制
该模块通过 全局请求拦截器 配合 MD5 签名工具类,实现了对所有关键请求的参数签名保护。
以下是基于 START 法则的深度分析:
S - Situation (情境)
充电桩控制涉及 资金结算(充值、扣费)和 物理安全(启动、停止高压设备)。 如果在公网环境下直接传输明文指令,面临两大安全风险:
- 参数篡改(Tampering):攻击者拦截请求后,修改充值金额(如 100元 -> 0.01元)或恶意停止他人正在充电的设备。
- 重放攻击(Replay):攻击者录制一次合法的“扣费请求”,然后多次重复发送,导致用户资金损失。
T - Task (任务)
需要构建一套轻量级但有效的 API 安全机制,目标是:
- 防篡改:确保服务器接收到的参数与客户端发送的完全一致,任何一位的改动都能被检测出来。
- 防重放:确保每个请求只能被执行一次,过期的请求应直接丢弃。
- 身份锚定:确保指令确实是由持有合法 Token 的用户发出的。
A - Action (行动)
我设计并实现了 “参数排序 + 时间戳 + MD5 签名” 的全链路校验机制。具体实现细节如下:
1. 签名工具类封装 (md5Utils.js)
为了保证签名结果的唯一性,必须对参数进行标准化处理:
- 字典序排序:
sortAsc方法将所有请求参数(包括 URL 参数和 Body 参数)按 Key 的 ASCII 码升序排列,消除参数顺序不同导致的签名差异。 - 参数合并:
getSign方法将urlParams(查询字符串)和requestParams(JSON Body)合并,确保所有业务参数都参与签名。 - MD5 计算:将排序后的 JSON 对象序列化为字符串,进行 MD5 哈希并转为大写。
代码证据:[md5Utils.js:L24-L34]
javascript
static getSign(url, requestParams) {
const urlParams = this.parseQueryString(url)
const jsonObj = this.mergeObject(urlParams, requestParams) // 合并参数
const requestBody = this.sortAsc(jsonObj) // 关键:字典序排序
return md5(JSON.stringify(requestBody)).toUpperCase() // 生成摘要
}2. 全局请求拦截注入 (request.js)
在 uni.$u.http.interceptors.request 中,对每一个非白名单请求进行统一处理,无需修改业务代码,实现了 AOP(面向切面) 式的安全增强。
- 时间戳防御:自动注入
timestamp参数,服务器可校验请求时间与服务器时间的偏差(如超过 60秒即拒绝),有效防止重放攻击。 - 签名注入:计算出的
sign值被放入 HTTP Header 中,与业务参数分离传输。
代码证据:[request.js:L33-L37]
javascript
const timestamp = new Date().getTime();
config.params.timestamp = timestamp; // 1. 注入时间戳,防重放
const sign = md5Utils.getSign(config.url, config.params); // 2. 计算签名
config.header.sign = sign; // 3. 设置请求头
config.header.timestamp = timestamp;R - Result (结果)
- 安全性闭环:任何对参数(如金额、设备号)的修改都会导致后端计算的 MD5 与 Header 中的
sign不匹配,请求被直接拦截。 - 开发零感知:安全逻辑完全封装在底层拦截器中,业务开发人员只需关注业务逻辑,无需手动处理签名,降低了接入成本和出错率。
- 高性价比:相比复杂的 RSA 非对称加密,MD5 签名在移动端计算极快,且能满足绝大多数防篡改需求,平衡了性能与安全。
T - Technology (技术/思考)
- 核心技术:
js-md5库、UniApp 拦截器、字典序算法。 - 设计思考:安全机制的设计不仅仅是算法的选择,更重要的是实现的完备性。比如这里将
URL Query和Body JSON同时纳入签名范围,防止了攻击者通过将参数在两者间移动来绕过校验的漏洞。同时,配合 Token 机制,构成了“身份 + 完整性 + 时效性”的三重防护体系。
