接口请求签名与认证的两种方案对比
目录
- 为什么一定要做“签名 + 认证”
- 方案一:自定义 Ed25519 报文级签名
- 方案二:JWT(Ed25519 EdDSA)
- 维度对照总表
- 场景化选型建议
- 结语
为什么一定要做「签名 + 认证」
认证 (Authentication):谁在访问?
完整性 (Integrity):请求内容在传输途中是否被篡改?
抗重放 (Replay):同一帧数据会不会被复制多次发动攻击?
只要是在公网、跨团队或跨语言调用 REST/GraphQL/gRPC/WebSocket,甚至 IoT MQTT,都绕不开上面三个问题。最常见做法就是“在每个请求上做签名,再让服务端验签”。下面给出两套可以直接落地的方案。
方案一:自定义 Ed25519 报文级签名
工作原理
- 客户端保存 私钥(48 B PKCS#8 DER,再 base64 存储)。
- 每次请求:
- 取 UTC 秒级时间戳
timestamp
bodyBytes = json.Marshal(body)
msg = timestamp || bodyBytes
signature = Ed25519.Sign(privKey, msg)
- 把 3 个头一并发出
X-Timestamp
X-Public-Key
(base64 DER,44 B)X-Signature
(base64,64 B)- (可选)再叠一层 Basic Auth / mTLS
- 取 UTC 秒级时间戳
- 服务端:
- 校验时间戳 ±N 秒
- 用
X-Public-Key
还原公钥 - 重新拼
msg′ = timestamp || body
,执行ed25519.Verify(...)
- 通过返回 200,失败直接 401/403
完整 Go 代码
客户端——生成头并发送
1 | // ---------------- 公共结构体 ---------------- |
服务端——验签中间件
1 | func verifyEd25519(next http.Handler) http.Handler { |
优缺点小结
优点
- 100 % Stateless,仅依赖 3 个自定义头
- 报文极小(~108 B)
- 仅依赖标准库
crypto/ed25519 + encoding/base64
缺点
- 只能证明“这条报文来自这把公钥”,无法嵌入角色、过期等信息
- 客户端换钥需要升级配置
- 无跨语言标准,需要自己维护 SDK 和文档
方案二:JWT(Ed25519 EdDSA 签名)
工作原理
Token = base64url(header).base64url(payload).base64url(signature)
- header:
alg=EdDSA
,可选kid
- payload:可存
sub
(主体)、exp
(过期)、role
(角色)、tenant_id
等 - signature:用同一把 Ed25519 私钥签名
- 客户端用
Authorization: Bearer <token>
携带
完整 Go 代码
客户端——签出 JWT
1 | // go get github.com/golang-jwt/jwt/v5 |
客户端——发送请求
1 | func SendJWTRequest(url, jwtToken string, body APIRequest) (string, string, error) { |
服务端——验证中间件
1 | func jwtMiddleware(pubDERBase64 string, next http.Handler) http.Handler { |
优缺点小结
优点
- payload 可自由写租户、角色、过期、jti(可做黑名单)
kid
+ JWKS 支持不停机密钥轮换- 生态成熟,浏览器 / 移动端 / Python / Java … 全有库
缺点
- Token 本身 200 ~ 800 B,占带宽
- 依赖第三方库
github.com/golang-jwt/jwt/v5
- 设计不当易造成权限泄漏(例如过期时间过长)
维度对照总表
维度 | 自定义 Ed25519 签名 | JWT / EdDSA |
---|---|---|
认证对象 | 公钥 == 身份,或再叠 Basic/mTLS | payload.sub / tenant / role 等 |
额外声明 | 几乎没有(可再造头) | payload 任意字段 |
抗重放 | 依赖时间戳 ±窗口 | exp / nbf / jti |
Key Rotation | 客户端手动换配置 | kid + JWKS 热替换 |
报文大小 | 3 个头约 108 B | Bearer 200–800 B |
跨语言生态 | 自写 SDK | jwt.io 完整生态 |
典型场景 | 内网 S2S、IoT 边缘、窄带场景 | BFF、移动端、三方开放 API |
场景化选型建议
- 单体或少量微服务、全部 Go 语言 —— 选 Ed25519 自定义签名,依赖少、带宽省。
- 面向前端 / 移动 / 第三方伙伴 —— 选 JWT,标准化、省对接成本。
- 需要抗重放 / 加权限 / 单点登录 —— 直接用 JWT,免造 jti / exp 机制。
- 极端窄带(LoRa、NB-IoT)或硬件受限 —— 继续用纯签名方案,甚至可再精简。
- 高安全场景 —— Ed25519 做“消息完整性”,外层叠 mTLS/JWT 做“身份 + 会话”,双保险。
结语
两条技术路线各有侧重:
- 自定义 Ed25519 →「最小依赖 + 最小报文 + 最小功能」
- JWT →「功能丰富 + 生态成熟 + 易扩展」
只要弄清业务边界、带宽约束、团队规模与未来演进,选型并不困难。希望本文能帮助 Go 开发者在“接口签名 / 认证”这道必考题上做出合适选择。Happy Coding!
接口请求签名与认证的两种方案对比
https://zion4h.github.io/2025/06/18/2025-6-18-接口请求签名与认证的两种方案对比/