基于Gin框架的Go语言JWT中间件实现及API权限保护问询
实现Gin框架下的JWT中间件保护API
我来一步步帮你搞定这个需求,用Gin框架实现JWT中间件来保护那些需要授权的接口,逻辑很清晰,咱们分步骤来:
1. 先安装依赖
首先得装JWT的Go库,目前常用的是golang-jwt/jwt/v5,还有Gin本身:
go get github.com/gin-gonic/gin go get github.com/golang-jwt/jwt/v5
2. 定义JWT核心配置和Claims结构体
先创建一个jwt_utils.go文件,用来放JWT相关的工具函数。首先定义密钥(生产环境建议从环境变量读取,这里先硬编码做示例),还有自定义的Claims,用来存用户的标识信息:
package main import ( "errors" "strings" "time" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) // 定义JWT密钥,生产环境建议从环境变量获取 var jwtSecret = []byte("your_strong_secret_key_here") // CustomClaims 自定义声明结构体,包含标准声明和自定义字段 type CustomClaims struct { UserID uint `json:"user_id"` Username string `json:"username"` jwt.RegisteredClaims } // GenerateToken 生成JWT Token func GenerateToken(userID uint, username string) (string, error) { // 设置Token过期时间,比如24小时 expireTime := time.Now().Add(24 * time.Hour) claims := CustomClaims{ UserID: userID, Username: username, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expireTime), IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: "your_app_name", }, } // 使用HS256算法创建Token token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(jwtSecret) }
3. 编写JWT验证中间件
还是在jwt_utils.go里,实现中间件函数,用来检查请求头里的Token是否有效:
// JWTAuthMiddleware JWT认证中间件 func JWTAuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // 从请求头里获取Authorization字段,格式是Bearer <token> authHeader := c.GetHeader("Authorization") if authHeader == "" { c.JSON(401, gin.H{"error": "请求头中缺少Authorization"}) c.Abort() return } // 分割Bearer和Token parts := strings.SplitN(authHeader, " ", 2) if !(len(parts) == 2 && parts[0] == "Bearer") { c.JSON(401, gin.H{"error": "Authorization格式错误"}) c.Abort() return } tokenStr := parts[1] claims := &CustomClaims{} // 解析Token token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) { // 验证算法是否正确 if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.New("无效的签名算法") } return jwtSecret, nil }) if err != nil { if errors.Is(err, jwt.ErrTokenExpired) { c.JSON(401, gin.H{"error": "Token已过期"}) } else { c.JSON(401, gin.H{"error": "Token验证失败"}) } c.Abort() return } if !token.Valid { c.JSON(401, gin.H{"error": "无效的Token"}) c.Abort() return } // 把解析后的用户信息放到上下文里,后续接口可以直接使用 c.Set("user_id", claims.UserID) c.Set("username", claims.Username) c.Next() } }
4. 修改登录接口,返回Token
在你的controller.Login函数里,当用户登录验证成功后,调用上面的GenerateToken生成Token并返回:
package controller import ( "net/http" "your_project_path" // 替换成你的项目模块路径 "github.com/gin-gonic/gin" ) func Login(c *gin.Context) { // 这里是你的登录逻辑:获取用户名密码,验证数据库中的用户 var loginReq struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` } if err := c.ShouldBindJSON(&loginReq); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"}) return } // 假设这里验证通过,获取到用户ID和用户名 userID := uint(1) // 实际从数据库查询 username := loginReq.Username // 生成Token token, err := main.GenerateToken(userID, username) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "生成Token失败"}) return } // 返回Token给前端 c.JSON(http.StatusOK, gin.H{ "message": "登录成功", "token": token, }) }
5. 给需要保护的路由添加中间件
修改你的Router.go,把JWTAuthMiddleware()应用到需要授权的接口上:
package main import ( "your_project_path/controller" // 替换成你的controller路径 "github.com/gin-gonic/gin" ) type Route struct { Name string Method string Pattern string HandlerFunc gin.HandlerFunc } func SetupRouter() *gin.Engine { r := gin.Default() // 公开路由:不需要授权 publicRoutes := []Route{ {"SaveUser", "POST", "/signup", controller.SaveUser}, {"LoginUser", "POST", "/login", controller.Login}, } // 受保护路由:需要JWT授权 protectedRoutes := []Route{ {"SaveCustomers", "POST", "/customer", controller.SaveCustomers}, {"GetCustomers", "GET", "/customer", controller.GetCustomers}, // 其他需要保护的接口... } // 注册公开路由 for _, route := range publicRoutes { switch route.Method { case "GET": r.GET(route.Pattern, route.HandlerFunc) case "POST": r.POST(route.Pattern, route.HandlerFunc) // 可以扩展其他方法 } } // 创建受保护的路由组,添加JWT中间件 protectedGroup := r.Group("/") protectedGroup.Use(JWTAuthMiddleware()) for _, route := range protectedRoutes { switch route.Method { case "GET": protectedGroup.GET(route.Pattern, route.HandlerFunc) case "POST": protectedGroup.POST(route.Pattern, route.HandlerFunc) // 其他方法同理 } } return r }
6. 在受保护接口中获取用户信息
如果你的业务接口需要用到当前登录用户的信息,可以直接从Gin的上下文里获取:
func GetCustomers(c *gin.Context) { userID, _ := c.Get("user_id") username, _ := c.Get("username") // 这里可以根据userID查询该用户的客户数据 c.JSON(http.StatusOK, gin.H{ "message": "获取客户列表成功", "user_id": userID, "username": username, // 客户数据... }) }
测试流程
- 调用
POST /signup注册用户(如果需要) - 调用
POST /login,传入正确的用户名密码,获取返回的Token - 调用受保护的接口(比如
GET /customer)时,在请求头里添加Authorization: Bearer <你的Token>,就能正常访问;如果Token无效/过期/缺失,会返回401错误
这样就完成了JWT中间件的实现,完美保护你的私有API啦!
内容的提问来源于stack exchange,提问作者Puneet
相关产品推荐
相关产品推荐

