Gin是Go语言生态中性能最高的HTTP框架之一,其核心设计是基于路由树的中间件链式调用。JWT(JSON Web Token)是无状态的认证方案,适合微服务和API网关架构。本文从Gin中间件原理、JWT签发验证到RBAC权限控制,完整拆解API认证授权的工程实现。
Gin中间件链式调用机制
Gin的中间件本质上是一个gin.HandlerFunc函数,通过Next()方法将控制权传递给链中的下一个处理函数。中间件按注册顺序执行,形成洋葱模型——请求阶段从外到内,响应阶段从内到外。这种设计允许在请求前后插入横切逻辑,如日志记录、认证校验、限流熔断等。
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
)
// 计时中间件
func TimingMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
// 传递控制权给下一个handler
c.Next()
elapsed := time.Since(start)
c.Header("X-Response-Time", elapsed.String())
fmt.Printf("%s %s %d %v\n",
c.Request.Method, c.Request.URL.Path,
c.Writer.Status(), elapsed)
}
}
// 请求ID中间件
func RequestIDMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
requestID := c.GetHeader("X-Request-ID")
if requestID == "" {
requestID = generateUUID()
}
c.Set("request_id", requestID)
c.Header("X-Request-ID", requestID)
c.Next()
}
}
func main() {
r := gin.Default()
// 全局中间件
r.Use(RequestIDMiddleware(), TimingMiddleware())
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "pong"})
})
r.Run(":8080")
}
JWT签发与验证实现
JWT由三部分组成:Header(算法和类型)、Payload(声明数据)和Signature(签名)。签发端用密钥对Header和Payload签名,验证端用同一密钥校验签名有效性。Go中常用的JWT库是golang-jwt/jwt/v5。
package auth
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
)
var jwtSecret = []byte("your-256-bit-secret-key")
// 自定义Claims
type Claims struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
Roles []string `json:"roles"`
jwt.RegisteredClaims
}
// 签发Token
func GenerateToken(userID int64, username string, roles []string) (string, error) {
claims := Claims{
UserID: userID,
Username: username,
Roles: roles,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "yunthe-api",
Subject: username,
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtSecret)
}
// 解析Token
func ParseToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return jwtSecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
Gin JWT认证中间件实现
将JWT验证封装为Gin中间件,从Authorization头提取Bearer Token并校验。验证通过后将用户信息注入Context供后续handler使用。
package middleware
import (
"net/http"
"strings"
"your-app/auth"
"github.com/gin-gonic/gin"
)
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "missing authorization header",
})
return
}
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "invalid authorization format",
})
return
}
claims, err := auth.ParseToken(parts[1])
if err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "invalid or expired token",
})
return
}
// 注入用户信息到Context
c.Set("user_id", claims.UserID)
c.Set("username", claims.Username)
c.Set("roles", claims.Roles)
c.Next()
}
}
// Refresh Token中间件
func RefreshToken() gin.HandlerFunc {
return func(c *gin.Context) {
refreshToken := c.GetHeader("X-Refresh-Token")
if refreshToken == "" {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"error": "missing refresh token",
})
return
}
claims, err := auth.ParseToken(refreshToken)
if err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "invalid refresh token",
})
return
}
// 签发新的access token
newToken, _ := auth.GenerateToken(claims.UserID, claims.Username, claims.Roles)
c.JSON(http.StatusOK, gin.H{"access_token": newToken})
}
}
RBAC权限控制中间件
基于角色的访问控制(RBAC)通过中间件实现路由级别的权限校验。从JWT中提取角色信息,校验当前请求路径是否在角色权限范围内。
package middleware
import (
"net/http"
"github.com/gin-gonic/gin"
)
// 角色权限映射
var rolePermissions = map[string][]string{
"admin": {"*"},
"editor": {"article:read", "article:write", "article:delete"},
"viewer": {"article:read"},
}
func RequirePermission(requiredPerm string) gin.HandlerFunc {
return func(c *gin.Context) {
roles, exists := c.Get("roles")
if !exists {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "no role information",
})
return
}
userRoles := roles.([]string)
for _, role := range userRoles {
perms, ok := rolePermissions[role]
if !ok {
continue
}
for _, p := range perms {
if p == "*" || p == requiredPerm {
c.Next()
return
}
}
}
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "insufficient permissions",
})
}
}
路由注册与中间件组合
package main
import (
"your-app/auth"
"your-app/handler"
"your-app/middleware"
"github.com/gin-gonic/gin"
)
func setupRouter() *gin.Engine {
r := gin.Default()
// 无认证路由组
public := r.Group("/api/v1")
{
public.POST("/login", handler.Login)
public.POST("/refresh", middleware.RefreshToken())
}
// 认证路由组
authed := r.Group("/api/v1")
authed.Use(middleware.JWTAuth())
{
authed.GET("/profile", handler.GetProfile)
// 文章管理:需要article:write权限
articles := authed.Group("/articles")
{
articles.GET("", middleware.RequirePermission("article:read"), handler.ListArticles)
articles.POST("", middleware.RequirePermission("article:write"), handler.CreateArticle)
articles.DELETE("/:id", middleware.RequirePermission("article:delete"), handler.DeleteArticle)
}
}
return r
}
// Login handler示例
func Login(c *gin.Context) {
var req struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
user, err := authenticate(req.Username, req.Password)
if err != nil {
c.JSON(401, gin.H{"error": "invalid credentials"})
return
}
accessToken, _ := auth.GenerateToken(user.ID, user.Username, user.Roles)
refreshToken, _ := auth.GenerateRefreshToken(user.ID, user.Username, user.Roles)
c.JSON(200, gin.H{
"access_token": accessToken,
"refresh_token": refreshToken,
"expires_in": 86400,
})
}
Gin的中间件链式设计提供了灵活的横切逻辑编排能力,JWT实现了无状态的认证方案。中间件组合JWTAuth + RequirePermission构成了完整的认证授权链路,路由组级别的中间件挂载实现了细粒度的权限控制。这套方案在微服务API网关、后台管理系统等场景中具备良好的扩展性——新增角色和权限只需更新rolePermissions映射表,中间件逻辑无需修改。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/go-yu-yan-gin-kuang-jia-zhong-jian-jian-lian-shi-she-ji-yu/