83 lines
2.2 KiB
Go
83 lines
2.2 KiB
Go
// 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
|
||
}
|