From c952c5d96c6f3bf779b8452459c9b0a0833bdeab Mon Sep 17 00:00:00 2001 From: Leon <147289645+LeoninCS@users.noreply.github.com> Date: Tue, 23 Dec 2025 20:48:41 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20jwt=E4=BC=98=E5=85=88=E4=BD=BF=E7=94=A8?= =?UTF-8?q?redis=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/middleware/jwt.go | 80 ++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/internal/middleware/jwt.go b/internal/middleware/jwt.go index b6bab26..50b54f5 100644 --- a/internal/middleware/jwt.go +++ b/internal/middleware/jwt.go @@ -2,18 +2,23 @@ package middleware import ( + "context" "errors" + "fmt" + "log" "net/http" "strings" - - "github.com/gin-gonic/gin" + "time" "feedsystem_video_go/internal/account" "feedsystem_video_go/internal/auth" + rediscache "feedsystem_video_go/internal/redis" + + "github.com/gin-gonic/gin" ) // JWTAuth check jwt token and ensure it matches the currently stored token. -func JWTAuth(accountRepo *account.AccountRepository) gin.HandlerFunc { +func JWTAuth(accountRepo *account.AccountRepository, cache *rediscache.Client) gin.HandlerFunc { return func(c *gin.Context) { authHeader := c.GetHeader("Authorization") if authHeader == "" { @@ -34,21 +39,11 @@ func JWTAuth(accountRepo *account.AccountRepository) gin.HandlerFunc { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid or expired token"}) return } - - accountInfo, err := accountRepo.FindByID(c.Request.Context(), claims.AccountID) - if err != nil || accountInfo.Token == "" || accountInfo.Token != tokenString { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "token has been revoked"}) - return - } - - c.Set("accountID", claims.AccountID) - c.Set("username", claims.Username) - - c.Next() + check(c, claims, tokenString, accountRepo, cache) } } -func SoftJWTAuth(accountRepo *account.AccountRepository) gin.HandlerFunc { +func SoftJWTAuth(accountRepo *account.AccountRepository, cache *rediscache.Client) gin.HandlerFunc { return func(c *gin.Context) { authHeader := c.GetHeader("Authorization") if authHeader == "" { @@ -70,18 +65,53 @@ func SoftJWTAuth(accountRepo *account.AccountRepository) gin.HandlerFunc { return } - accountInfo, err := accountRepo.FindByID(c.Request.Context(), claims.AccountID) - if err != nil || accountInfo.Token == "" || accountInfo.Token != tokenString { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "token has been revoked"}) - return - } - - c.Set("accountID", claims.AccountID) - c.Set("username", claims.Username) - - c.Next() + check(c, claims, tokenString, accountRepo, cache) } } + +func check(c *gin.Context, claims *auth.Claims, tokenString string, accountRepo *account.AccountRepository, cache *rediscache.Client) { + key := fmt.Sprintf("account:%d", claims.AccountID) + + // 先查 Redis + if cache != nil { + cacheCtx, cancel := context.WithTimeout(c.Request.Context(), 50*time.Millisecond) + defer cancel() + + b, err := cache.GetBytes(cacheCtx, key) + if err == nil { + if string(b) != tokenString { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "token has been revoked"}) + return + } + c.Set("accountID", claims.AccountID) + c.Set("username", claims.Username) + c.Next() + return + } + } + + // Redis 故障/未启用:查 DB 兜底 + accountInfo, err := accountRepo.FindByID(c.Request.Context(), claims.AccountID) + if err != nil || accountInfo.Token == "" || accountInfo.Token != tokenString { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "token has been revoked"}) + return + } + + if cache != nil { + cacheCtx, cancel := context.WithTimeout(c.Request.Context(), 50*time.Millisecond) + defer cancel() + + if err := cache.SetBytes(cacheCtx, key, []byte(tokenString), 24*time.Hour); err != nil { + log.Printf("failed to set cache: %v", err) + } + } + + c.Set("accountID", claims.AccountID) + c.Set("username", claims.Username) + c.Next() + +} + func GetAccountID(c *gin.Context) (uint, error) { uidValue, exists := c.Get("accountID") if !exists {