★ 鉴权设计:Token 校验中间件与 RBAC 思路

一句话结论:鉴权拆成两层——认证(Authentication:你是谁)与授权(Authorization:你能干什么)。认证用 Token 校验中间件:从 Authorization: Bearer 头取 JWT,固定算法白名单验签、校验过期等声明,把用户身份写进 gin.Context,白名单路由不挂中间件。授权按 RBAC 模型落地:用户→角色→权限(操作+资源),权限数据登录后缓存或放进 JWT 声明,handler 按所需权限码校验;同时必须做资源级越权检查(IDOR),不能只验“登录了”。

Token 校验中间件

var jwtKey = []byte(os.Getenv("JWT_SECRET")) // 32+ 字节,勿硬编码

func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        h := c.GetHeader("Authorization")
        if !strings.HasPrefix(h, "Bearer ") { abort401(c); return }
        token, err := jwt.ParseWithClaims(strings.TrimPrefix(h, "Bearer "),
            &Claims{}, func(t *jwt.Token) (any, error) {
                if t.Method != jwt.SigningMethodHS256 { // 防算法混淆
                    return nil, errors.New("unexpected signing method")
                }
                return jwtKey, nil
            }, jwt.WithValidMethods([]string{"HS256"}))
        if err != nil || !token.Valid { abort401(c); return }
        claims := token.Claims.(*Claims)
        c.Set("uid", claims.UserID)
        c.Set("roles", claims.Roles)
        c.Next()
    }
}
  • 用 jwt.WithValidMethods 固定算法,杜绝 alg=none、RS/HS 混淆攻击。
  • 过期/登出吊销:短有效期 token + refresh token 轮换;要即时吊销就维护 Redis 黑名单(登出/改密入黑名单,TTL=剩余有效期)。

RBAC 落地思路

内容说明
模型用户、角色、权限 + 两张关联表权限=资源+操作(如 order:create)
载入登录后缓存 user:{id}:perms,或放 JWT 声明缓存即时生效但每次查询;JWT 免查询但不即时、易膨胀
校验路由级中间件或 handler 内 CheckPerm路由登记“该操作需要的权限码”,集中比对
越权资源归属校验查 order 前校验 order.UserID == uid,防 IDOR

注册与使用

r.POST("/api/v1/login", login) // 公开
api := r.Group("/api/v1", AuthMiddleware())
api.GET("/orders", RequirePerm("order:read"), listOrders)
api.POST("/orders/:id/cancel", RequirePerm("order:cancel"), cancelOrder)

追问记忆点

  • 追问:JWT 放 Redis 黑名单和直接查库角色哪个快?——黑名单只需一次 Redis GET;但每次请求都验权限可以再叠加进程内缓存+TTL。
  • 追问:认证过了还要防什么?——越权:同接口横向(他人资源)、纵向(低角色调高权限动作),都要资源归属与权限码双校验。
  • 追问:token 存哪更安全?——SPA 存内存/HttpOnly Cookie(防 XSS 偷),Cookie 方案注意 CSRF(配 SameSite)。
  • 记忆点:认证管身份、授权管权限;验签锁算法、声明校验过期;RBAC=角色挂权限,权限码登记路由;别忘了 IDOR。
笔记加载中…