Beego中JWT鉴权必须在BaseController.Prepare()中实现,因Prepare是Controller实例化后、方法执行前的唯一可靠入口,支持token提取(Authorization头)、ParseWithClaims安全验签、iss/aud/exp校验及显式this.Abort("401")终止;不可用全局Filter,因其时序早于路由解析且无法阻止后续逻辑执行。

Beego 中 JWT 鉴权必须用 Prepare,不能靠中间件自动拦截
Beego 的「中间件」(如 beego.InsertFilter)在请求进入路由前执行,但此时 this.Ctx.Input.ControllerName 和 this.Ctx.Input.MethodName 还未解析完成,无法准确判断是否该放行或校验;而 Prepare() 是 Controller 实例化后、任何 Get/Post 方法调用前的统一入口,天然支持 token 提取、解析、绑定用户数据和提前终止。
常见错误是把 JWT 解析逻辑写在全局 Filter 里,结果发现 this.Ctx.Input.Param(":id") 拿不到、this.Ctx.Request.Body 已被读空、或者校验失败后仍继续执行业务方法——因为 Filter 里调用 ctx.StopRun() 不会阻止 Controller 的 Prepare 和后续方法运行。
- 所有鉴权逻辑必须放在
BaseController.Prepare()中,业务 Controller 统一继承它 -
Prepare内必须显式调用this.Abort("401")或this.Abort("403"),否则流程不会中断 - 不要在
Prepare里重复解析 body 或 query,Beego 已在 Controller 初始化阶段做过一次解析 - 登录接口(如
/v1/auth/login)需在Prepare开头加路径白名单判断,直接return
jwt.ParseWithClaims 是唯一安全的解析方式,jwt.Parse 必须禁用
jwt.Parse 会尝试用任意算法(包括 "none")解码 header,攻击者可构造 Header{"alg":"none"} 的伪造 token 绕过签名验证。Beego 鉴权中一旦用了 jwt.Parse,上线后大概率出现横向越权或 401 飘忽不定。
正确做法是强制指定算法,并在密钥回调函数中返回固定类型密钥:
- HS256 场景:用
jwt.ParseWithClaims(tokenStr, claims, func(*jwt.Token) (interface{}, error) { return []byte(secretKey), nil }) - RS256 场景:回调函数内必须判断
token.Method.Alg(),且返回*rsa.PublicKey类型,不能返回interface{}或[]byte - 永远不传
nil当密钥回调,那等价于允许任意算法 - 密钥不能从环境变量或配置文件实时读取——每次解析都调用
os.Getenv或beego.AppConfig.String会拖慢性能且易出错,应启动时加载并缓存
Token 必须从 Authorization: Bearer xxx 提取,其他位置一律不认
JWT 最佳实践明确要求 token 放在 Authorization Header 中,格式为 Bearer <token></token>。Beego 里若从 Cookie、Query 或 Form 取 token,不仅违反 RFC 7519,还会引入 CSRF 风险(Cookie 自动携带)、泄露风险(Query 被日志/代理记录)、以及解析歧义(Form 可能被 multipart 解析污染)。
提取逻辑要严格:
- 用
this.Ctx.Input.Header.Get("Authorization")获取原始值 - 用
strings.TrimPrefix(authHeader, "Bearer ")去掉前缀,注意空格不能少 - 校验去掉前缀后长度是否 > 0,空字符串或只有空格必须拒绝
- 禁止 fallback 到
this.Ctx.Input.Cookie("token")或this.GetString("token") - 如果前端非要塞在自定义 Header(如
X-Auth-Token),可以接受,但需在文档中明确定义,且服务端不兼容Authorization就等于放弃标准互操作性
验签后必须手动校验 issuer、audience 和 exp,claims.Valid() 不够用
claims.Valid() 只检查 exp、nbf、iat 时间字段,完全不碰 iss(签发者)、aud(受众)、sub(主体)等业务关键声明。微服务场景下,一个 token 若没校验 aud == "user-service",就可能被订单服务误认并放行,造成越权访问。
Beego Controller 中的校验要点:
- 提取
claims.Issuer并比对预设值(如"auth-server.example.com"),不匹配则this.Abort("401") - 提取
claims.Audience,用claims.VerifyAudience(audience, true)严格校验,第三个参数true表示必须精确匹配 -
exp字段本身没问题,但要注意 jwt-go 默认用time.Now().Unix()比较,若服务器时间不同步(偏差 > 1s),会导致大量误拒;生产环境必须确保 NTP 同步 - 不要把用户角色、权限列表全塞进 JWT payload——体积膨胀、Redis 缓存压力大、且无法动态回收;只放
sub(用户 ID)和必要短生命周期字段(如role),权限查 DB 或 Redis
Prepare 是执行点,ParseWithClaims 是验签底线,Authorization Header 是传输契约,iss/aud/exp 是业务边界。漏掉任何一个,都不是“功能可用”,而是“带洞上线”。


















