go语言jwt如何做到无感刷新Token

Go语言中,使用Gin框架来实现JWT无感刷新Token的功能,可以结合golang-jwt/jwt库来生成和验证JWT。以下是一个基于Gin框架的JWT无感刷新Token的实现示例:

首先,你需要安装所需的依赖包:

go get -u github.com/gin-gonic/gin

go get -u github.com/golang-jwt/jwt/v4

然后,创建一个简单的Gin服务器,实现JWT的签发和刷新功能:

package main  
  
import (  
	"encoding/json"  
	"fmt"  
	"net/http"  
	"time"  
  
	"github.com/gin-gonic/gin"  
	"github.com/golang-jwt/jwt/v4"  
)  
  
const (  
	jwtSecret     = "your_secret_key" // 密钥,实际使用时应该保密  
	jwtAccessTokenExpiration = 1 * time.Hour  
	jwtRefreshTokenExpiration = 7 * 24 * time.Hour  
)  
  
type CustomClaims struct {  
	jwt.RegisteredClaims  
	RefreshToken string `json:"refresh_token,omitempty"`  
}  
  
// GenerateJWT 生成JWT Access Token和Refresh Token  
func GenerateJWT(userID string) (accessToken, refreshToken string, err error) {  
	now := time.Now()  
	accessTokenClaims := &CustomClaims{  
		RegisteredClaims: jwt.RegisteredClaims{  
			ExpiresAt: jwt.NewNumericDate(now.Add(jwtAccessTokenExpiration)),  
			Issuer:    "auth.example.com",  
			Subject:   userID,  
		},  
	}  
	accessTokenToken := jwt.NewWithClaims(jwt.SigningMethodHS256, accessTokenClaims)  
	accessTokenString, err := accessTokenToken.SignedString([]byte(jwtSecret))  
	if err != nil {  
		return "", "", err  
	}  
  
	refreshTokenClaims := &CustomClaims{  
		RegisteredClaims: jwt.RegisteredClaims{  
			ExpiresAt: jwt.NewNumericDate(now.Add(jwtRefreshTokenExpiration)),  
			Issuer:    "auth.example.com",  
			Subject:   userID,  
		},  
		RefreshToken: GenerateRandomString(32), // 假设有一个生成随机字符串的函数  
	}  
	refreshTokenToken := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshTokenClaims)  
	refreshTokenString, err := refreshTokenToken.SignedString([]byte(jwtSecret))  
	if err != nil {  
		return "", "", err  
	}  
  
	return accessTokenString, refreshTokenClaims.RefreshToken, nil  
}  
  
// GenerateRandomString 生成随机字符串,用于Refresh Token  
func GenerateRandomString(length int) string {  
	// 假设有一个生成随机字符串的函数,这里使用简单的方式代替  
	const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"  
	randStr := make([]byte, length)  
	for i := range randStr {  
		randStr[i] = chars[randInt63n(int64(len(chars)))]  
	}  
	return string(randStr)  
}  
  
// randInt63n 是一个简单的随机数生成函数,用于演示  
func randInt63n(n int64) int {  
	// 在实际应用中,请使用更安全的随机数生成器,例如crypto/rand  
	return int(time.Now().UnixNano() % n)  
}  
  
// RefreshTokenHandler 处理刷新Token的请求  
func RefreshTokenHandler(c *gin.Context) {  
	var refreshTokenRequest struct {  
		RefreshToken string `json:"refresh_token" binding:"required"`  
	}  
  
	if err := c.ShouldBindJSON(&refreshTokenRequest); err != nil {  
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})  
		return  
	}  
  
	// TODO: 在实际应用中,你需要在数据库中验证refresh token的有效性  
	// 这里只是简单示例,假设refreshTokenRequest.RefreshToken是有效的  
  
	userID := "user_123" // 假设这是与refresh token关联的用户ID  
	newAccessToken, _, err := GenerateJWT(userID)  
	if err != nil {  
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})  
		return  
	}  
  
	c.JSON(http.StatusOK, gin.H{"access_token": newAccessToken})  
}  
  
func main() {
r := gin.Default()
// 登录接口,假设它返回JWT的Access Token和Refresh Token  
r.POST("/login", func(c *gin.Context) {  
	// 这里应该是实际的用户认证逻辑  
	// ...  

	// 假设用户认证成功,生成JWT Tokens  
	accessToken, refreshToken, err := GenerateJWT("user_123")  
	if err != nil {  
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})  
		return  
	}  

	c.JSON(http.StatusOK, gin.H{  
		"access_token":  accessToken,  
		"refresh_token": refreshToken,  
	})  
})  

// 刷新Token接口  
r.POST("/refresh", RefreshTokenHandler)  

// 其他需要认证保护的接口  
authMiddleware := func(c *gin.Context) {  
	tokenString := c.Request.Header.Get("Authorization")  
	if len(tokenString) == 0 {  
		c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Authorization token missing"})  
		return  
	}  

	splitToken := strings.Split(tokenString, "Bearer ")  
	if len(splitToken) != 2 {  
		c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token format"})  
		return  
	}  

	token, err := jwt.ParseWithClaims(splitToken[1], &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {  
		return []byte(jwtSecret), nil  
	})  

	if err != nil {  
		c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})  
		return  
	}  

	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {  
		c.Set("userID", claims.Subject)  
		c.Next()  
	} else {  
		c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Token invalid or expired"})  
	}  
}  

protectedRoute := r.Group("/protected")  
protectedRoute.Use(authMiddleware)  
{  
	protectedRoute.GET("/resource", func(c *gin.Context) {  
		userID := c.MustGet("userID").(string)  
		c.JSON(http.StatusOK, gin.H{"message": "Protected resource", "user_id": userID})  
	})  
}  

// 启动服务器  
r.Run(":8080")

这个示例中,我们定义了一个`GenerateJWT`函数来生成Access Token和Refresh Token,并在登录接口中使用它。

 

`RefreshTokenHandler`函数处理刷新Token的请求,验证旧的Refresh Token,并返回新的Access Token。 我们还创建了一个`authMiddleware`中间件来验证Access Token。

这个中间件检查请求的`Authorization`头部,解析JWT,并验证其有效性和过期时间。

如果Token有效,中间件会将用户ID设置到Gin上下文中,以便后续的处理程序使用。

请注意,在实际应用中,你需要在数据库中存储和验证Refresh Token的有效性,并且在用户注销时删除它们。

此外,应使用更安全的方法来生成随机字符串,例如使用`crypto/rand`包。 上述代码仅为示例,并未涵盖所有可能的错误处理和边界情况。在实际应用中,你需要根据具体需求进行适当的修改和增强。

 

版权声明:
作者:89391311
链接:https://www.csev.cn/code-2/golang/20240424292.html/
来源:测试分享
版权声明:本文欢迎任何形式转载,转载时完整保留本声明信息(包含原文链接、原文出处、原文作者、版权声明)即可。本文后续所有修改都会第一时间在原始地址更新。
THE END
根据我国《计算机软件保护条例》第十七条规定:“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。本站资源仅供个人学习交流,请于下载后 24 小时内删除,不允许用于商业用途,否则法律问题自行承担。
分享
二维码
打赏
< <上一篇
下一篇>>
文章目录
关闭
目 录