// 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 }