158 lines
3.7 KiB
Go
158 lines
3.7 KiB
Go
|
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密钥已从环境变量加载")
|
|||
|
}
|
|||
|
}
|