项目结构更新
This commit is contained in:
106
utill/jwt/jwtUt.go
Normal file
106
utill/jwt/jwtUt.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"time"
|
||||
"toutoukan/init/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. 存储到Redis(键:jwt:{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 {
|
||||
fmt.Println("验证请求")
|
||||
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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user