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
来源:彩色动力-测试分享
版权声明:本文欢迎任何形式转载,转载时完整保留本声明信息(包含原文链接、原文出处、原文作者、版权声明)即可。本文后续所有修改都会第一时间在原始地址更新。
作者:89391311
链接:https://www.csev.cn/code-2/golang/20240424292.html
来源:彩色动力-测试分享
版权声明:本文欢迎任何形式转载,转载时完整保留本声明信息(包含原文链接、原文出处、原文作者、版权声明)即可。本文后续所有修改都会第一时间在原始地址更新。
THE END
二维码
打赏
文章目录
关闭
共有 0 条评论