basic 和 bearer 认证必须严格区分前缀(含空格)、禁止共用解析逻辑;需校验 authorization 头完整性、base64 解码安全性、digest 字段合规性,并注意反向代理透传与 nonce 状态管理。

Basic 和 Bearer 不能共用同一段解析逻辑
一个请求只该有一种认证方式,但现实里常出现客户端误发两个头、或中间件叠加导致 Authorization 头被覆盖/混淆。直接用 strings.HasPrefix(authHeader, "Bearer") 会把 "Basic dXNlcjpwYXNz" 也当 Bearer 处理——因为前缀匹配不严格。
- 必须先检查前缀是否为
"Basic "(注意末尾空格)或"Bearer ",大小写敏感,且空格不可省略 - 若同时存在两个头,Go 的
http.Header.Get("Authorization")只返回最后一个,无法感知冲突;需在日志中记录重复头现象 - 不要依赖
strings.Split(authHeader, " ")—— 客户端可能加多余空格,应先strings.TrimSpace()再切分 - 切分后必须校验长度:至少两段,第二段非空;否则直接 401,不进后续解码
Base64 解码失败时别只判 err != nil
base64.StdEncoding.DecodeString() 对损坏编码(比如少一位、含非法字符)会返回 base64.CorruptInputError,但这个 error 是 wrapping 类型,不能靠 == 判断。
- 正确做法是用
errors.Is(err, base64.CorruptInputError),而不是err != nil - 空字符串、全空白、超长(如 > 2048 字符)的 base64 片段也应提前拒绝,避免解码开销
- 解码后字节长度为 0 或不含
:时,立刻视为凭证格式错误,不尝试strings.SplitN(decoded, ":", 2) - 密码字段允许为空,但业务层必须显式校验——
http.BasicAuth返回空密码不会报错,你得自己拦
Digest Auth 必须手动解析,标准库完全不支持
Go 的 net/http 没有 Digest 相关函数,连 WWW-Authenticate 头都要手拼。这不是遗漏,是刻意为之:Digest 协议依赖 nonce 管理、qop 校验、HA1/HA2 计算,状态和安全边界极难收敛。
- 客户端发来的
Authorization: Digest ...必须按 RFC 7616 逐字段解析:username、realm、nonce、uri、response、qop、nc、cnonce -
uri字段必须和r.URL.RequestURI()完全一致(包括 query 参数顺序与编码),任何差异都拒绝 -
nonce必须带服务端可验证签名(如base64(sha256(timestamp + secret + clientIP))),不能只是时间戳或随机数 -
nc(请求计数)要绑定nonce存储,并只接受严格递增;跳变或回退即重放攻击,立即拉黑 IP
反向代理下 Authorization 头经常“消失”
Nginx、Traefik 等默认不转发 Authorization 头,尤其当配置了 proxy_set_header 但漏掉这一项时,Go 服务根本收不到头——本地 curl 能过,上线就 401。
- Nginx 配置里必须显式加:
proxy_set_header Authorization $http_authorization; - 如果用了
http.StripPrefix,认证逻辑一定要放在 strip 后的 handler 里,否则r.URL.Path还带前缀,路径匹配失效 - 调试时打印
r.Header全量内容,确认Authorization是否到达 Go 进程,而不是只查r.Header.Get("Authorization") - 某些 CDN 或网关会把
Authorization: Bearer xxx改成X-Authorization: xxx,需提前约定并适配
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











