签名验证必须放在middleware层,且在路由匹配之后、controller执行之前;推荐用party.use绑定到具体路由组,避免误拦健康检查等非业务接口。

签名验证逻辑该放在哪一层
必须在路由匹配之后、控制器执行之前做签名校验,否则可能绕过验证。Iris 的 Use 和 UseGlobal 都可以,但推荐用 Use 绑定到具体路由组,避免对健康检查、静态资源等非业务接口误判。
常见错误是把签名中间件写在 UseGlobal 里又没过滤路径,结果 /favicon.ico 或 /health 返回 401,排查半天才发现是签名头缺失导致的拦截。
- 签名字段建议统一放请求头,比如
X-Signature、X-Timestamp、X-Nonce - 时间戳偏差超过 300 秒(5 分钟)应直接拒绝,防止重放攻击
- Nonce 值需服务端记录并短期去重(如 Redis TTL 5 分钟),不能只靠客户端传
怎么生成和比对签名值
签名算法本身不复杂,关键是参数顺序、编码、密钥管理要一致。推荐用 HMAC-SHA256,原始数据拼接格式为:method|path|timestamp|nonce|body(body 需规范化:去空格、排序 query 参数、JSON body 要用标准序列化,比如 json.Marshal 后的字节流)。
容易踩的坑是直接对 ctx.PostBody() 做哈希——如果请求体被中间件提前读取(如启用 ctx.ReadJSON),再读就是空;或者用了 gzip 编码但没解压就签名,导致前后不一致。
- 务必在签名中间件中调用
ctx.ResetBodyReader()确保后续控制器还能读 body - 若需兼容 form-data,签名应基于
ctx.FormValue提取的字段构造,而非 raw body - 密钥不要硬编码,从环境变量或配置中心加载,例如
os.Getenv("API_SECRET")
如何让中间件支持 GET/POST/PUT 等不同方法
GET 请求没有 body,签名只能基于 query string;而 POST/PUT 通常含 body。中间件必须区分处理,不能一概而论。
典型错误是统一取 ctx.PostBody(),导致 GET 请求永远验签失败;或者忽略 query 参数,使 /api/user?id=123 和 /api/user?id=456 签名相同。
- GET/HEAD:拼接
ctx.Request().URL.Query().Encode()作为 data part - POST/PUT/PATCH:先尝试
ctx.IsRequestBodyReader,再按 content-type 分支处理(application/json→ JSON 字节流,application/x-www-form-urlencoded→ 排序后的键值对字符串) - 所有方法都必须包含
ctx.Path()和ctx.Method(),防止路径混淆攻击
签名中间件怎么返回友好错误且不暴露细节
验签失败时不能返回 500 或堆栈,更不能泄露“时间戳超时”“nonce 已存在”这类信息——这等于帮攻击者调试。
统一返回 401 + 空响应体,或极简提示如 {"error":"unauthorized"}。日志里才记详细原因,且敏感字段(如 nonce、原始签名)需脱敏。
- 避免在响应头中返回
X-Debug-Sign类字段,测试环境也别开 - 如果用
ctx.StopExecution()中断流程,确保前面没写过 response body,否则会 panic - 对高频失败 IP 建议加限流(如
iris.Limiter),防暴力试探签名规则











