Skip to content

Gin 实战 11:接口鉴权中间件

登录负责签发 Token,接口鉴权负责在请求进入业务代码前校验 Token,并把当前用户写入 gin.Context

1. 请求头格式

text
Authorization: Bearer <accessToken>

2. 解析 Token

go
func ParseToken(tokenString string, secret string) (*Claims, error) {
    // ParseWithClaims 会解析 token,并把 payload 填充到 Claims 结构体。
    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (any, error) {
        // 返回签名密钥,JWT 库会用它校验签名是否可信。
        return []byte(secret), nil
    })
    if err != nil {
        return nil, err
    }

    // 类型断言失败或 token.Valid=false,都说明 Token 不可信。
    claims, ok := token.Claims.(*Claims)
    if !ok || !token.Valid {
        return nil, errors.New("无效 Token")
    }
    return claims, nil
}

3. Gin 中间件

go
// CurrentUserKey 是写入 gin.Context 的 key。
// 后续 Handler 可以通过它获取当前登录用户。
const CurrentUserKey = "currentUser"

func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 1. 从请求头读取 Authorization。
        header := c.GetHeader("Authorization")
        if !strings.HasPrefix(header, "Bearer ") {
            c.Error(errs.BadRequest("Token 无效"))
            c.Abort()
            return
        }

        // 2. 去掉 Bearer 前缀,拿到真正的 JWT 字符串。
        tokenString := strings.TrimPrefix(header, "Bearer ")

        // 3. 校验签名和过期时间,并解析用户信息。
        claims, err := ParseToken(tokenString, config.Cfg.Security.JWT.SecretKey)
        if err != nil {
            c.Error(errs.BadRequest("Token 无效或已过期"))
            c.Abort()
            return
        }

        // 4. 写入上下文,后续 Handler 就能拿到当前用户。
        c.Set(CurrentUserKey, claims)

        // 5. 放行请求,继续执行后续 Handler。
        c.Next()
    }
}

4. 路由分组

go
func Register(r *gin.Engine) {
    api := r.Group("/api/v1")

    // 登录和验证码不需要 Token,所以挂在公开路由上。
    auth.RegisterRoutes(api) // 登录、验证码公开

    // 受保护路由统一挂认证中间件。
    authorized := api.Group("")
    authorized.Use(middleware.AuthMiddleware())
    {
        user.RegisterRoutes(authorized)
    }
}

5. 获取当前用户

go
func CurrentUser(c *gin.Context) (*Claims, bool) {
    // 从 gin.Context 读取认证中间件写入的用户信息。
    value, ok := c.Get(CurrentUserKey)
    if !ok {
        return nil, false
    }

    // 做类型断言,避免取到错误类型导致 panic。
    user, ok := value.(*Claims)
    return user, ok
}

到这里,用户 CRUD 已经从公开接口变成登录后才能访问的受保护接口。

基于 MIT 许可发布 · 如需部署协助或二开定制,请查看 支持与合作