如何在Golang中实现JWT认证_Web Token认证流程

8次阅读

JWT签名验证必须用ParseWithClaims而非Parse,因Parse仅解码不校验签名;敏感字段应存Claims而非URL参数,payload无加密;需设exp/iat;刷新Token须用HttpOnly cookie存储并每次失效;应使用github.com/golang-jwt/jwt/v5库。

如何在Golang中实现JWT认证_Web Token认证流程

JWT签名验证必须用ParseWithClaims而非Parse

直接调用Parse只会解码token结构,不校验签名,攻击者可篡改payload后重签(若你误用对称密钥且未设SigningMethod约束,风险更高)。必须显式指定jwt.MapClaims并传入密钥和验证逻辑:

token, err := jwt.ParseWithClaims(tokenString, &jwt.MapClaims{}, func(token *jwt.Token) (Interface{}, error) {     if _, ok := token.Method.(*jwt.SigningMethodHmac); !ok {         return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])     }     return []byte(secretKey), nil })

常见错误:漏掉token.Method类型检查,导致RSA公钥被误用于HMAC验证;或把secretKey写成硬编码字符串却没做环境隔离。

用户ID等敏感字段应存进Claims而非URL参数

JWT的payload是Base64Url编码、**无加密**的,任何中间人可解码查看。把user_idrole塞进自定义Claims没问题,但绝不能放密码哈希、手机号明文、邮箱全量字符串:

  • expiat必须设置,否则过期校验失效
  • 自定义字段名避免和标准字段冲突(如别叫isssub除非真按RFC 7519语义用)
  • 如果需要权限分级,用scope数组比单个role字符串更易扩展

示例安全写法:

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

claims := jwt.MapClaims{     "user_id": 123,     "scope":   []string{"read:order", "write:cart"},     "exp":     time.Now().Add(24 * time.Hour).unix(),     "iat":     time.Now().Unix(), }

刷新Token需用RefreshToken双令牌机制

仅靠一个长期有效的access Token存在泄露后无法主动作废的问题。正确做法是颁发短期Access Token(如15分钟)+ 长期Refresh Token(如7天),且Refresh Token必须:

  • 存储在HttpOnly + Secure Cookie中,禁止js访问
  • 每次使用后立即失效(服务端记录已用过的Refresh Token hash,或绑定设备指纹)
  • 与Access Token分离存储——不要把Refresh Token塞进JWT的payload

刷新接口典型流程:

func refreshHandler(w http.ResponseWriter, r *http.Request) {     cookie, err := r.Cookie("refresh_token")     if err != nil {         http.Error(w, "no refresh token", http.StatusUnauthorized)         return     }     // 1. 校验cookie中的refresh_token是否有效且未被使用     // 2. 生成新access_token(含新exp)     // 3. 在DB中标记该refresh_token为已使用     // 4. 返回新access_token(不返回新refresh_token,除非显式请求轮换) }

Go JWT库选github.com/golang-jwt/jwt/v5而非旧版

旧版github.com/dgrijalva/jwt-go已被弃用,存在关键漏洞(如CVE-2020-26160:None算法绕过)。v5版强制要求显式声明SigningMethod,默认禁用none算法,并修复了时钟偏移校验缺陷:

  • 导入路径必须是github.com/golang-jwt/jwt/v5,不是v4或无版本号
  • ParseWithClaims第二个参数必须是指针类型(&jwt.MapClaims{}),否则v5会panic
  • 校验时间时,v5默认允许1s误差,若需更严格,用WithValidator自定义

最容易被忽略的是:v5的SigningMethodHS256.KeyFunc返回值必须是interface{},不是[]byte——传错类型会导致签名永远不匹配。

text=ZqhQzanResources