You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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,
        // 客户数据...
    })
}

测试流程

  1. 调用POST /signup注册用户(如果需要)
  2. 调用POST /login,传入正确的用户名密码,获取返回的Token
  3. 调用受保护的接口(比如GET /customer)时,在请求头里添加Authorization: Bearer <你的Token>,就能正常访问;如果Token无效/过期/缺失,会返回401错误

这样就完成了JWT中间件的实现,完美保护你的私有API啦!

内容的提问来源于stack exchange,提问作者Puneet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:17:54