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 {