From 6d63455ee46ca75b994f5200dd427f428a6c0962 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Tue, 16 Jul 2024 13:48:59 +0800 Subject: [PATCH] feat: token refresh && expiration time --- internal/middleware/jwt/jwt.go | 6 ++---- internal/router/api/v1/api.go | 4 ++++ internal/service/user/login.go | 34 ++++++++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/internal/middleware/jwt/jwt.go b/internal/middleware/jwt/jwt.go index 71b1f95..d32ac36 100644 --- a/internal/middleware/jwt/jwt.go +++ b/internal/middleware/jwt/jwt.go @@ -6,7 +6,6 @@ import ( "strconv" "time" - "github.com/TensoRaws/NuxBT-Backend/dal/model" "github.com/TensoRaws/NuxBT-Backend/module/config" "github.com/TensoRaws/NuxBT-Backend/module/log" "github.com/golang-jwt/jwt/v5" @@ -23,15 +22,14 @@ func GetJWTSigningKey() []byte { } // GenerateToken 生成 jwt(json web token) -func GenerateToken(u *model.User) string { - userID := strconv.FormatInt(int64(u.UserID), 10) +func GenerateToken(userID int32) string { claims := jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(GetJWTTokenExpiredDuration())), NotBefore: jwt.NewNumericDate(time.Now()), Issuer: "TensoRaws", IssuedAt: jwt.NewNumericDate(time.Now()), Subject: "token", - ID: userID, // jwt 中保存合法用户的 ID + ID: strconv.FormatInt(int64(userID), 10), // jwt 中保存合法用户的 ID } // 使用指定的签名算法创建用于签名的字符串对象,使用 json 序列化和 base64Url 编码生成 jwt 的 1、2 部分 diff --git a/internal/router/api/v1/api.go b/internal/router/api/v1/api.go index fca2a86..a85817c 100644 --- a/internal/router/api/v1/api.go +++ b/internal/router/api/v1/api.go @@ -35,6 +35,10 @@ func NewAPI() *gin.Engine { user.POST("register", user_service.Register) // 用户登录 user.POST("login", user_service.Login) + // 用户刷新 token + user.POST("token/refresh", + jwt.RequireAuth(cache.Clients[cache.JWTBlacklist], true), // 把 token 拉黑 + user_service.TokenRefresh) // 用户登出 user.POST("logout", jwt.RequireAuth(cache.Clients[cache.JWTBlacklist], true), // 把 token 拉黑 diff --git a/internal/service/user/login.go b/internal/service/user/login.go index 640bb42..b805a06 100644 --- a/internal/service/user/login.go +++ b/internal/service/user/login.go @@ -15,7 +15,8 @@ type LoginRequest struct { } type LoginResponse struct { - Token string `json:"token"` + Expiration int64 `json:"expiration"` + Token string `json:"token"` } // Login 用户登录 (POST /login) @@ -37,13 +38,38 @@ func Login(c *gin.Context) { err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)) if err == nil { // 注册之后的下次登录成功,才会为其生成 token - token := jwt.GenerateToken(user) - // 打印相应信息和用户信息以及生成的 token 值 + token := jwt.GenerateToken(user.UserID) + + claims, err := jwt.ParseToken(token) + if err != nil { + resp.AbortWithMsg(c, code.UnknownError, err.Error()) + return + } + resp.OKWithData(c, LoginResponse{ - Token: token, + Expiration: claims.ExpiresAt.Unix(), + Token: token, }) } else { resp.Abort(c, code.UserErrorInvalidPassword) return } } + +// TokenRefresh 用户刷新 token (POST /token/refresh) +func TokenRefresh(c *gin.Context) { + userID, _ := resp.GetUserIDFromGinContext(c) + + token := jwt.GenerateToken(userID) + + claims, err := jwt.ParseToken(token) + if err != nil { + resp.AbortWithMsg(c, code.UnknownError, err.Error()) + return + } + + resp.OKWithData(c, LoginResponse{ + Expiration: claims.ExpiresAt.Unix(), + Token: token, + }) +}