
Beego中间件里必须用 jwt.ParseWithClaims 而不是 jwt.Parse
直接调用 jwt.Parse 会触发算法协商漏洞:攻击者可构造 {"alg":"none"} 的 Header,绕过签名验证,导致任意用户登录。Beego 中间件必须显式指定算法和密钥,且只允许一种签名方式。
-
jwt.ParseWithClaims(tokenString, claims, func(*jwt.Token) (interface{}, error) { return []byte(secretKey), nil })是唯一安全写法 - 若用 RS256,回调函数中需判断
token.Method.Alg()并返回*rsa.PublicKey,不能硬编码[]byte - 禁止在回调里动态拼接密钥(如从 DB 查、拼接字符串),易引发 panic 或泄露
Token 必须从 Authorization Header 提取,且格式校验要严格
Beego 的 this.Ctx.Input.Header.Get("Authorization") 返回的是原始字符串,常见错误是没做前缀剥离或空值判断,导致后续解析 panic 或静默失败。
- 必须用
strings.TrimPrefix(authHeader, "Bearer ")去掉前缀,不能用strings.Split(authHeader, " ")—— 某些客户端可能加多个空格或 tab - 提取后要检查
len(tokenString) == 0,否则ParseWithClaims会返回ValidationError: empty token - 绝对不要从
Cookie或Query读取 JWT —— 这既违反 RFC 7519 最佳实践,也打开 CSRF 和日志泄露风险
验签通过后,claims.Valid() 不够,必须手动校验 Issuer 和 Audience
Beego Controller 的 Prepare 方法里解析完 token 后,很多人只调 claims.Valid() 就放行,但该方法只检查 exp/nbf/iat,不验证业务身份上下文。
- 必须显式比对
claims.Issuer == "your-api-domain"和claims.Audience == []string{"beego-service"} - 如果微服务间调用,
Audience应为下游服务名,而非上游;否则横向越权风险极高 - 建议把 issuer/audience 写进 Beego 配置项(如
app.conf中的jwt.issuer = "auth.example.com"),避免硬编码
中间件里 this.Abort("401") 必须紧跟验签失败逻辑,且不能依赖 Prepare 自动终止
Beego 的 Prepare 是钩子函数,不是拦截器 —— 它不会自动阻止后续 Get/Post 执行。一旦验签失败却没调 Abort,请求会继续进入业务逻辑,造成未授权访问。
- 每次解析失败(
err != nil或!claims.Valid()或 issuer/audience 不匹配)后,必须立即this.Abort("401") - 不要用
this.Data["json"] = ...; this.ServeJSON()替代Abort—— 这会导致状态码仍是 200,前端难感知错误 - 注意:Beego v2.x 中
this.Abort不再接受字符串参数,应改用this.Abort(401)
Beego 不处理密钥轮换,也不自动透传 Token 到下游 HTTP 请求 —— 这两点在微服务场景下极易被忽略,得靠你自己在 Controller 里手动注入 req.Header.Set("Authorization", "Bearer "+token),且确保是在 http.NewRequest 构造阶段,而非修改 this.Ctx.Request.Header。











