This commit is contained in:
toom1996
2026-08-26 10:45:21 +08:00
parent 3cf1368e68
commit 30c9f21da4
48 changed files with 3442 additions and 0 deletions

82
internal/pkg/jwt/jwt.go Normal file
View File

@ -0,0 +1,82 @@
// Package jwt 封装 JWT 令牌的签发与解析。
//
// Claims 结构(uid / username / email / exp)与签名算法(HS256)保持与原项目一致,
// 因此原后端签发的 token 在本服务中依然有效(相同 secret 前提下)。
package jwt
import (
"errors"
"time"
jwtlib "github.com/golang-jwt/jwt/v5"
)
// ErrInvalidToken 表示令牌缺失、过期或签名不合法。
var ErrInvalidToken = errors.New("invalid token")
// Claims 从令牌中解析出的用户身份信息。
type Claims struct {
UserID uint32
Username string
Email string
}
// Manager 令牌签发器。通过构造函数注入密钥与有效期,避免每次调用都重新读取配置
// (原项目在 genToken/中间件里各自调用 config.Load(),属于重复解析)。
type Manager struct {
secret []byte
expire time.Duration
}
// NewManager 创建令牌签发器。expireHours <= 0 时回落为 7 天。
func NewManager(secret string, expireHours int) *Manager {
if expireHours <= 0 {
expireHours = 168
}
return &Manager{
secret: []byte(secret),
expire: time.Duration(expireHours) * time.Hour,
}
}
// Generate 签发令牌。
func (m *Manager) Generate(userID uint32, username, email string) (string, error) {
claims := jwtlib.MapClaims{
"uid": userID,
"username": username,
"email": email,
"exp": time.Now().Add(m.expire).Unix(),
}
return jwtlib.NewWithClaims(jwtlib.SigningMethodHS256, claims).SignedString(m.secret)
}
// Parse 校验并解析令牌。
func (m *Manager) Parse(tokenStr string) (*Claims, error) {
token, err := jwtlib.Parse(tokenStr, func(t *jwtlib.Token) (any, error) {
// 只接受 HMAC 签名,防止 alg 混淆攻击
if _, ok := t.Method.(*jwtlib.SigningMethodHMAC); !ok {
return nil, jwtlib.ErrSignatureInvalid
}
return m.secret, nil
})
if err != nil || !token.Valid {
return nil, ErrInvalidToken
}
raw, ok := token.Claims.(jwtlib.MapClaims)
if !ok {
return nil, ErrInvalidToken
}
c := &Claims{}
// JSON 数字统一解析为 float64
if uid, ok := raw["uid"].(float64); ok {
c.UserID = uint32(uid)
}
if v, ok := raw["username"].(string); ok {
c.Username = v
}
if v, ok := raw["email"].(string); ok {
c.Email = v
}
return c, nil
}