鉴权中间件:Bearer Token 解析、角色校验、401 统一返回

上一章会签发 JWT,这一章把它做成 Gin 中间件:拦截请求、解析 Authorization 头、把用户信息写进上下文,并统一返回 401。中间件写法以 gin 官方文档的「中间件」章节为准。

1. 中间件骨架

Gin 中间件就是返回 gin.HandlerFunc 的函数,c.Next() 之前是前置校验:

func AuthRequired() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := BearerToken(c.GetHeader("Authorization"))
        if token == "" {
            abort401(c, "缺少认证信息")
            return
        }
        claims, err := ParseToken(token) // 复用上一章的校验函数
        if err != nil {
            abort401(c, "Token 无效或已过期")
            return
        }
        c.Set("userID", claims.UserID)
        c.Set("role", claims.Role)
        c.Next() // 放行给后续 handler
    }
}

func BearerToken(h string) string {
    if len(h) > 7 && h[:7] == "Bearer " {
        return h[7:]
    }
    return ""
}

2. 角色校验

再叠一层只关心角色的中间件,用可变参数支持多种角色:

func RequireRoles(roles ...string) gin.HandlerFunc {
    return func(c *gin.Context) {
        role := c.GetString("role")
        for _, r := range roles {
            if role == r {
                c.Next()
                return
            }
        }
        c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "无权限访问"})
    }
}

只允许管理员:RequireRoles("admin");多个角色都行就多传几个。

3. 401 统一返回

把认证失败的响应收敛成一个函数,保证格式一致、前端好解析:

func abort401(c *gin.Context, msg string) {
    c.Header("WWW-Authenticate", `Bearer realm="api"`)
    c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
        "code":  401,
        "error": msg,
    })
}

4. 挂载到路由

中间件按组生效:公开接口不挂,受保护接口挂:

api := r.Group("/api")
{
    api.POST("/login", LoginHandler)             // 公开
    authed := api.Group("", AuthRequired())      // 需要登录
    {
        authed.GET("/me", MeHandler)
        admin := authed.Group("", RequireRoles("admin"))
        admin.DELETE("/users/:id", DeleteUserHandler)
    }
}

注意点

  • 校验失败必须 AbortWithStatusJSONreturn,否则请求会继续往后执行。
  • c.Set 的键建议用常量而不是裸字符串,避免拼写不一致取不到值。
  • 401(未认证)与 403(无权限)语义要分清,别一律 401。
  • 中间件里别做重活(如再查一次库),能靠 Token 声明判断就靠声明。

小结

鉴权中间件 = 前置解析 + 上下文注入 + 统一失败响应。把「谁(userID)+ 什么角色(role)」放进 c,业务 handler 只管取用。角色再多可以引入 RBAC 策略点,但骨架不变。

笔记加载中…