Files
toutoukan/utill/jwtUt.go
2025-08-10 05:57:51 +08:00

106 lines
2.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package utill
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"time"
"toutoukan/config"
"toutoukan/init/redisInit"
"toutoukan/model/usermodel"
)
// 生成JWT并存储到Redis
func GenerateJWTAndStore(openid string) (string, error) {
// 1. 生成JWT
expirationTime := time.Now().Add(2 * time.Hour)
claims := &usermodel.JWTClaims{
OpenID: openid,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "toutoukan",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(config.Conf.Jwtsecret))
if err != nil {
return "", err
}
// 2. 存储到Redisjwt:{token}openid过期时间与JWT一致
redisKey := fmt.Sprintf("jwt:%s", tokenString)
err = redisInit.RedisClient.Set(
redisInit.Ctx,
redisKey,
openid,
2*time.Hour,
).Err()
return tokenString, err
}
// 验证JWT并检查Redis
func ValidateJWTWithRedis(tokenString string) (*usermodel.JWTClaims, error) {
// 1. 先验证JWT签名和过期时间
token, err := jwt.ParseWithClaims(
tokenString,
&usermodel.JWTClaims{},
func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, jwt.ErrSignatureInvalid
}
return []byte(config.Conf.Jwtsecret), nil
},
)
if err != nil {
return nil, err
}
// 2. 验证Redis中是否存在防止已登出的令牌
redisKey := fmt.Sprintf("jwt:%s", tokenString)
exists, err := redisInit.RedisClient.Exists(redisInit.Ctx, redisKey).Result()
if err != nil {
return nil, fmt.Errorf("redis查询失败: %w", err)
}
if exists == 0 {
return nil, fmt.Errorf("令牌已失效")
}
// 3. 验证声明有效性
if claims, ok := token.Claims.(*usermodel.JWTClaims); ok && token.Valid {
return claims, nil
}
return nil, jwt.ErrSignatureInvalid
}
// 辅助函数:从请求头提取令牌
func ExtractTokenFromHeader(c *gin.Context) string {
authHeader := c.GetHeader("Authorization")
var tokenString string
fmt.Sscanf(authHeader, "Bearer %s", &tokenString)
return tokenString
}
// JWT+Redis验证中间件
func JWTAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
tokenString := ExtractTokenFromHeader(c)
if tokenString == "" {
c.JSON(401, gin.H{"error": "请先登录", "code": "10033"})
c.Abort()
return
}
claims, err := ValidateJWTWithRedis(tokenString)
if err != nil {
c.JSON(401, gin.H{"error": "令牌无效或已过期", "code": "10034"})
c.Abort()
return
}
c.Set("openid", claims.OpenID)
c.Next()
}
}