package util import ( "ego/pkg/logger" "fmt" "os" "sync" "time" "github.com/golang-jwt/jwt/v4" "go.uber.org/zap" ) const ( TokenExpireTime = 24 * time.Hour // Token 过期时间(24小时) // TokenGroup token TokenGroup = "user_token:" ) var ( // JwtKey JWT签名密钥 JwtKey []byte jwtKeyOnce sync.Once ) // Claims JWT声明结构 type Claims struct { ID string `json:"id"` Username string `json:"username"` jwt.RegisteredClaims } // initJwtKey 懒加载方式初始化JWT密钥 func initJwtKey() { jwtKeyOnce.Do(func() { secret := os.Getenv("JWT_SECRET") if secret == "" { secret = "default-jwt-secret-please-change-in-production" // 注意:这里不能使用logger,因为可能还没初始化 // 如果需要日志,在配置初始化完成后再输出 } JwtKey = []byte(secret) }) } // getJwtKey 获取JWT密钥,确保已初始化 func getJwtKey() []byte { initJwtKey() return JwtKey } // GenerateToken 生成JWT Token func GenerateToken(id, username string) (string, error) { if id == "" || username == "" { return "", fmt.Errorf("用户ID和用户名不能为空") } key := getJwtKey() expireTime := time.Now().Add(TokenExpireTime) claims := &Claims{ ID: id, Username: username, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expireTime), IssuedAt: jwt.NewNumericDate(time.Now()), NotBefore: jwt.NewNumericDate(time.Now()), Issuer: "ego-system", Subject: username, }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString(key) if err != nil { // 安全地记录错误,如果logger已初始化 if logger.Logger != nil { logger.Error(nil, "JWT Token生成失败", zap.Error(err)) } return "", fmt.Errorf("token生成失败: %w", err) } return tokenString, nil } // ParseToken 解析JWT Token func ParseToken(tokenString string) (jwt.MapClaims, error) { if tokenString == "" { return nil, fmt.Errorf("token不能为空") } key := getJwtKey() token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("无效的签名方法: %v", token.Header["alg"]) } return key, nil }) if err != nil { // 安全地记录错误,如果logger已初始化 if logger.Logger != nil { logger.Error(nil, "JWT Token解析失败", zap.Error(err)) } return nil, fmt.Errorf("token解析失败: %w", err) } if !token.Valid { return nil, fmt.Errorf("token无效或已过期") } claims, ok := token.Claims.(jwt.MapClaims) if !ok { return nil, fmt.Errorf("token claims格式错误") } return claims, nil } // ValidateToken 验证Token有效性 func ValidateToken(tokenString string) (*Claims, error) { if tokenString == "" { return nil, fmt.Errorf("token不能为空") } key := getJwtKey() token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (any, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("无效的签名方法: %v", token.Header["alg"]) } return key, nil }) if err != nil { // 安全地记录错误,如果logger已初始化 if logger.Logger != nil { logger.Error(nil, "JWT Token验证失败", zap.Error(err)) } return nil, fmt.Errorf("token验证失败: %w", err) } if claims, ok := token.Claims.(*Claims); ok && token.Valid { return claims, nil } return nil, fmt.Errorf("token无效") } // LogJwtKeyStatus 在配置初始化完成后输出JWT密钥状态日志 func LogJwtKeyStatus() { secret := os.Getenv("JWT_SECRET") if secret == "" { logger.Warn(nil, "使用默认JWT密钥,生产环境请设置JWT_SECRET环境变量") } else { logger.Info(nil, "JWT密钥已从环境变量加载") } }