Golang Web开发如何做鉴权_Golang JWT鉴权实现

5次阅读

不要自己实现 JWT 的 Parse 和 Sign,应使用 github.com/golang-jwt/jwt/v5;需显式指定有效算法、校验时间漂移、安全存储密钥、规范提取 Token、正确处理错误、避免 JWT 存 refresh token、合理分组路由并同步服务器时钟。

Golang Web开发如何做鉴权_Golang JWT鉴权实现

JWT 鉴权要不要自己实现 ParseSign

不要。Go 生态里 github.com/golang-jwt/jwt/v5(原 dgrijalva/jwt-go 维护分支)已足够稳定,且修复了旧版关键安全问题(如 alg: none 漏洞)。自己手写签名/解析极易出错,尤其在密钥管理、时间校验、claim 验证逻辑上。

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • jwt.ParseWithClaims + 自定义 jwt.Claims 结构体,别用 map[String]Interface{} —— 类型安全、字段可验证
  • 必须显式设置 WithValidMethods([]string{"HS256"}),禁用弱算法
  • 签发时用 time.Now().Add(24 * time.Hour) 而非固定时间戳,避免时钟漂移导致 token 立即失效
  • 密钥别硬编码:从环境变量读取 os.Getenv("JWT_SECRET"),开发时可用 .env 文件,但上线务必走 secret manager

http 中间件里怎么提取并校验 token?

常见错误是只检查 Authorization: Bearer xxx 头存在,就直接解析 —— 实际可能 header 缺失、格式错误、token 过期或签名无效,这些都该统一拦截返回 401。

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • r.Header.Get("Authorization") 取值后,用 strings.CutPrefix(authHeader, "Bearer ") 提取 token,别用 strings.Split —— 防止空格分割异常
  • 调用 token.Valid 前,先检查 token != nil && err == nilerrjwt.ValidationError 时,用 errors.Is(err, jwt.ErrTokenExpired) 区分过期和其他错误
  • 校验通过后,把用户 ID 或角色存进 context.WithValue(r.Context(), keyUser, userID),后续 handler 直接取,别重复解析 token

刷新 token(Refresh Token)该不该用 JWT?

不推荐用 JWT 存储 refresh token。JWT 一旦签发就不可撤销,而 refresh token 必须支持主动作废(比如用户登出、密码修改)。把它当普通字符串redis 更可控:key 为 refresh:{userID}:{fingerprint},value 存随机 UUID,TTL 设为 7 天,并在每次刷新时更新 TTL 和生成新 token。

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • 登录接口返回两个 token:access_token(短时效,如 15 分钟)和 refresh_token(长时效,如 7 天),后者仅 https 传输、HttpOnly cookie 发送
  • 刷新接口需校验旧 refresh_token 是否存在于 Redis,且关联的 user_id 仍有效(比如检查账号是否被禁用)
  • 刷新成功后,立刻删掉旧 refresh_token 的 Redis key,再写入新 key —— 防止重放攻击

为什么有些请求能绕过中间件鉴权?

典型原因是路由注册顺序不对:比如用 http.HandleFunc("/api/", ...) 注册了通配前缀,但没对 /api/auth/login 这类免鉴权路径做显式排除,导致中间件误判。或者用了 gorilla/mux、gin 等框架却忘了给路由组加 Use()UseMiddleware()

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • 用框架路由分组(如 Gin 的 auth := r.Group("/api")),只对需要鉴权的组调用中间件,登录/注册等路径单独挂到根组
  • 中间件里加日志:log.printf("auth middleware: path=%s method=%s", r.URL.Path, r.Method),快速定位漏掉的路由
  • 测试时用 curl 手动发一个无 token 的 GET /api/user/profile,确认返回 401;再发带非法 token 的请求,确认不是 500 而是 401 —— 后者说明中间件捕获了解析错误

实际部署时最容易忽略的是时钟同步:如果服务器时间比 NTP 慢几分钟,会导致刚签发的 token 被判定为“尚未生效”(jwt.ValidationErrorNotValidYet)。上线前务必运行 timedatectl status 并启用 chronydntpd

text=ZqhQzanResources