返回
简单讲讲我在Gin中使用JWT的续签思路
后端
2024-01-27 12:09:44
在Gin中使用JWT进行认证
JWT(JSON Web Token)是一种流行的认证机制,可以用来验证用户身份并授权访问受保护的资源。在Gin中使用JWT进行认证非常简单,我们可以使用一个现成的库,比如gin-gonic/gin-jwt。
import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin-jwt"
)
func main() {
r := gin.Default()
// 设置JWT中间件
authMiddleware, err := ginjwt.New(&ginjwt.GinJWTMiddleware{
Realm: "my realm",
Key: []byte("my secret key"),
Timeout: time.Hour,
MaxRefresh: time.Hour * 24,
})
if err != nil {
panic(err)
}
// 认证路由组
auth := r.Group("/auth")
auth.POST("/login", authMiddleware.LoginHandler)
// 私有路由组
private := r.Group("/private")
private.Use(authMiddleware.MiddlewareFunc())
private.GET("/data", func(c *gin.Context) {
c.JSON(200, gin.H{
"data": "hello world",
})
})
r.Run()
}
这个代码创建了一个Gin路由器,并使用gin-gonic/gin-jwt库设置了JWT中间件。我们可以在/auth/login
路由上进行登录,成功登录后会返回一个JWT令牌。这个令牌可以用来访问/private
路由组下的受保护资源。
JWT的续签方案
JWT令牌通常都有一个有限的有效期,为了保持用户登录状态,我们需要在令牌过期之前对其进行续签。通常的做法是让前端在令牌即将过期时向服务端发送请求,服务端收到请求后会生成一个新的令牌返回给前端。
但是,这种方法存在一个问题,那就是服务端需要保存令牌的状态,这可能会导致性能问题。为了解决这个问题,我设计了一个简单的续签方案,不需要服务端保存令牌的状态。
这个方案的关键在于,我们会在JWT令牌中存储一个刷新令牌。刷新令牌是一个比JWT令牌更长的令牌,它可以用来生成新的JWT令牌。当JWT令牌即将过期时,前端可以向服务端发送请求,服务端收到请求后会验证刷新令牌,如果刷新令牌有效,则会生成一个新的JWT令牌返回给前端。
这样,我们就避免了服务端需要保存令牌的状态,也避免了前端需要过多的配合。
这个续签方案的具体实现可以参考以下代码:
import (
"fmt"
"time"
"github.com/golang-jwt/jwt"
)
func main() {
// 生成JWT令牌
jwtToken, err := generateJWTToken("username", "user@example.com")
if err != nil {
panic(err)
}
// 生成刷新令牌
refreshToken, err := generateRefreshToken("username", "user@example.com")
if err != nil {
panic(err)
}
// 在JWT令牌中存储刷新令牌
jwtToken.Claims.(jwt.MapClaims)["refreshToken"] = refreshToken
fmt.Println("JWT令牌:", jwtToken)
fmt.Println("刷新令牌:", refreshToken)
// 续签JWT令牌
newJWTToken, err := renewJWTToken(jwtToken, refreshToken)
if err != nil {
panic(err)
}
fmt.Println("新的JWT令牌:", newJWTToken)
}
func generateJWTToken(username, email string) (*jwt.Token, error) {
claims := jwt.MapClaims{
"username": username,
"email": email,
"exp": time.Now().Add(time.Hour).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte("my secret key"))
}
func generateRefreshToken(username, email string) (string, error) {
return uuid.NewV4().String(), nil
}
func renewJWTToken(jwtToken *jwt.Token, refreshToken string) (*jwt.Token, error) {
claims := jwtToken.Claims.(jwt.MapClaims)
// 验证刷新令牌
if claims["refreshToken"] != refreshToken {
return nil, errors.New("invalid refresh token")
}
// 生成新的JWT令牌
newClaims := jwt.MapClaims{
"username": claims["username"],
"email": claims["email"],
"exp": time.Now().Add(time.Hour).Unix(),
}
newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, newClaims)
return newToken.SignedString([]byte("my secret key"))
}
这个代码演示了如何生成JWT令牌、刷新令牌,以及如何使用刷新令牌续签JWT令牌。
总结
在本文中,我讨论了如何在Gin中使用JWT进行认证,以及我为实现JWT续签而想出的一个简单的方案。这个方案不需要服务端保存Token的状态,前端也不需要过多的配合。