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