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

JWT 鉴权要不要自己实现 Parse 和 Sign?
不要。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 == nil;err为jwt.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 并启用 chronyd 或 ntpd。










