问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

JWT Token刷新实战:Go语言版

创作时间:
作者:
@小白创作中心

JWT Token刷新实战:Go语言版

引用
CSDN
6
来源
1.
https://blog.csdn.net/Gaorui678/article/details/139030661
2.
https://blog.csdn.net/weixin_44102152/article/details/138667173
3.
https://blog.csdn.net/hzb869168467/article/details/136310155
4.
https://www.cnblogs.com/XY-Heruo/p/18280787
5.
https://www.cnblogs.com/QiaoPengjun/p/18630890
6.
https://www.cnblogs.com/failymao/p/18059012

在现代Web应用开发中,JWT(JSON Web Tokens)已经成为主流的认证解决方案。然而,JWT Token的过期问题一直是开发者需要解决的重要课题。本文将通过Go语言及其流行的Gin框架,详细讲解如何高效可靠地实现JWT Token刷新功能。

JWT Token刷新原理

在JWT认证体系中,通常会使用两种Token:Access Token和Refresh Token。

  • Access Token:用于访问受保护资源的凭据,有效期较短(如15分钟),以提高安全性。
  • Refresh Token:用于在Access Token过期后获取新的Access Token,有效期较长,有时甚至是永久有效的。

刷新流程如下:

  1. 用户初次登录时,服务器返回Access Token和Refresh Token。
  2. 客户端在每次请求时携带Access Token。
  3. 当Access Token过期时,客户端使用Refresh Token请求新的Access Token。
  4. 服务器验证Refresh Token有效后,返回新的Access Token。
  5. 如果Refresh Token也过期或无效,用户需要重新登录。

Go语言实现JWT Token刷新

环境准备

首先,确保已经安装了必要的Go包:

go get -u github.com/gin-gonic/gin
go get -u github.com/golang-jwt/jwt/v5

代码实现

  1. 定义JWT相关结构体
package main

import (
    "github.com/golang-jwt/jwt/v5"
    "time"
)

// JWTClaims 包含用户ID和过期时间
type JWTClaims struct {
    UserID uint `json:"user_id"`
    jwt.RegisteredClaims
}

// JWTSecret 签名密钥
var JWTSecret = []byte("your-secret-key")
  1. 生成JWT Token
func GenerateToken(userID uint) (string, error) {
    claims := JWTClaims{
        UserID: userID,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
        },
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(JWTSecret)
}
  1. 刷新JWT Token
func RefreshToken(oldToken string) (string, error) {
    token, err := jwt.ParseWithClaims(oldToken, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
        return JWTSecret, nil
    })

    if err != nil {
        return "", err
    }

    if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
        newClaims := JWTClaims{
            UserID: claims.UserID,
            RegisteredClaims: jwt.RegisteredClaims{
                ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
            },
        }

        newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, newClaims)
        return newToken.SignedString(JWTSecret)
    }

    return "", errors.New("invalid token")
}
  1. Gin 路由配置
func main() {
    r := gin.Default()

    // 公开接口:登录
    r.POST("/login", func(c *gin.Context) {
        // 这里应该包含验证用户名和密码的逻辑
        // 假设验证通过,生成Token
        token, err := GenerateToken(1) // 假设用户ID为1
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
            return
        }
        c.JSON(http.StatusOK, gin.H{"token": token})
    })

    // 受保护接口:需要JWT认证
    protected := r.Group("/api")
    protected.Use(AuthMiddleware())
    {
        protected.GET("/user", func(c *gin.Context) {
            c.JSON(http.StatusOK, gin.H{"message": "Welcome!"})
        })
    }

    // Token刷新接口
    r.POST("/refresh_token", func(c *gin.Context) {
        var req struct {
            Token string `json:"token" binding:"required"`
        }
        if err := c.ShouldBindJSON(&req); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        newToken, err := RefreshToken(req.Token)
        if err != nil {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
            return
        }
        c.JSON(http.StatusOK, gin.H{"token": newToken})
    })

    r.Run(":8080")
}
  1. JWT认证中间件
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        tokenString := c.GetHeader("Authorization")
        if tokenString == "" {
            c.AbortWithStatus(http.StatusUnauthorized)
            return
        }

        token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
            return JWTSecret, nil
        })

        if err != nil || !token.Valid {
            c.AbortWithStatus(http.StatusUnauthorized)
            return
        }

        claims, ok := token.Claims.(*JWTClaims)
        if !ok {
            c.AbortWithStatus(http.StatusUnauthorized)
            return
        }

        c.Set("user_id", claims.UserID)
        c.Next()
    }
}

安全考虑

  1. 保护Refresh Token:由于Refresh Token具有长期有效性,应妥善存储,避免在前端存储,建议存储在服务器端或使用HTTP-only Cookies。
  2. 传输安全:所有涉及Token的请求都应通过HTTPS进行,防止中间人攻击。
  3. 限制刷新次数:可以设置Refresh Token的使用次数限制,进一步增强安全性。
  4. 定期更新Refresh Token:即使Refresh Token有效期较长,也应定期更新,避免长期固定不变。

通过以上实现,我们可以在Go语言中高效地处理JWT Token的刷新逻辑,既保证了用户认证的安全性,又提升了用户体验。希望这些代码示例能帮助你更好地理解和实现JWT Token刷新功能。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号