http.BasicAuth 不能直接当认证开关用,因为它仅解析并解码 Authorization 头,不校验空密码、不处理解码错误、不设置 WWW-Authenticate 响应头,也无任何安全检查。

Go 语言里做 HTTP Basic 认证,关键不是“设个头就完事”,而是必须手动解析 + 显式校验 + 正确响应;否则 http.BasicAuth 只是摆设,Authorization 头可能被代理丢掉、解码失败不报错、空密码直接放行。
为什么 http.BasicAuth 函数不能直接当认证开关用
http.BasicAuth 只做一件事:从 r.Header.Get("Authorization") 中提取并 base64 解码,返回 username 和 password 字符串。它不检查是否为空、不验证合法性、不返回错误、也不写响应头。
- 如果客户端发
Authorization: Basic dXNlcjo=(即user:),http.BasicAuth返回username="user",password=""—— 你若没判空,就等于允许空密码登录 - 如果 base64 编码损坏(比如少一位),它静默返回空字符串,不会 panic,也不会告诉你哪里错了
- 它不处理
WWW-Authenticate响应头,401 也得你自己写:w.Header().Set("WWW-Authenticate", `Basic realm="api"`) - 典型误用:
user, pass := r.BasicAuth(); if user == "admin" { next.ServeHTTP(w, r) }—— 这里完全没校验pass,也没拒绝非法输入
手动解析 Authorization 头的正确步骤
绕过 http.BasicAuth 直接解析,控制力更强,也更容易加日志、限流或兼容非标准客户端。
- 先取头:
authHeader := r.Header.Get("Authorization"),空值直接 401 - 按空格分割,检查前缀:
if !strings.HasPrefix(authHeader, "Basic ")→ 拒绝,避免把Bearer xxx当 Basic 处理 - 取 base64 部分:
cred := strings.TrimPrefix(authHeader, "Basic "),再用base64.StdEncoding.DecodeString(cred) - 解码失败时,用
errors.Is(err, base64.CorruptInputError)判断,不要只写err != nil - 解码后按第一个
:分割用户名密码:parts := strings.SplitN(decoded, ":", 2),len(parts) != 2就算格式错误
StripPrefix 或反向代理下 Basic Auth 失效的真相
常见现象:本地 curl 能过,上线 Nginx 后总 401;或者 /api/users 路由加了认证,但请求 /api/users/(带尾斜杠)就跳过校验。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
-
http.StripPrefix("/api", h)后,r.URL.Path是去前缀后的路径(如/users),但你的认证中间件如果写在StripPrefix外层,它看到的还是/api/users—— 路径判断失效,甚至根本没进校验逻辑 - Nginx 默认不转发
Authorization头,需显式配置:proxy_set_header Authorization $http_authorization;,否则 Go 进程收不到该头 - 调试时别只打
r.URL.Path,要打印r.Header全量内容,确认Authorization是否到达 - 生产建议:对所有未带
Authorization的GET /api/.*请求打 warn 日志,快速暴露代理漏配
Go 客户端发 Basic Auth 请求时重定向丢头的问题
用 http.Client 请求一个会 301 跳转到 HTTPS 的地址,目标服务返回 401 —— 不是你服务写错了,是 Go 客户端默认不带 Authorization 头重试。
- 根本原因:
http.Client.CheckRedirect默认策略会丢弃敏感头,包括Authorization - 修复方式:自定义重定向函数,显式复制头:
client := &http.Client{<br> CheckRedirect: func(req *http.Request, via []*http.Request) error {<br> req.Header.Set("Authorization", via[0].Header.Get("Authorization"))<br> return nil<br> },<br>} - 注意:仅复制
via[0]的头,避免多跳后头污染;且只在可信域内这么做,防止凭据泄露到第三方 - curl 默认不自动跳转,所以本地测试正常 ≠ 线上正常,务必模拟跳转链路验证
真正容易被忽略的是:Basic Auth 本身无状态、无过期,一旦凭证泄露就是长期风险;Authorization 头出现在访问日志里、代理缓存中、APM 工具里都可能被记录——哪怕你代码写对了,部署链路上任何一个环节没关日志或没配 HTTPS,整个认证就形同虚设。

















