Files
backend_v2/internal/pkg/jwt/jwt.go
toom1996 9c3403a903 update
2026-08-30 10:45:34 +08:00

77 lines
2.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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(签发令牌)已随注册 / 登录接口一并移除——当前服务没有任何签发入口,
// 只保留 Parse 供 middleware.Auth 校验既有令牌。
// 将来接入登录时恢复:构造 jwtlib.MapClaims{"uid","username","email","exp"},
// 再用 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
}