// Package jwt 访问令牌(短时,存内存)与刷新令牌(HttpOnly Cookie)签发/校验。 package jwt import ( "errors" "time" "github.com/golang-jwt/jwt/v5" ) type Claims struct { UserID uint64 `json:"uid"` Username string `json:"uname"` Role string `json:"role"` jwt.RegisteredClaims } type Manager struct { secret []byte issuer string accessTTL time.Duration refreshTTL time.Duration } func NewManager(secret, issuer string, accessTTL, refreshTTL time.Duration) *Manager { return &Manager{secret: []byte(secret), issuer: issuer, accessTTL: accessTTL, refreshTTL: refreshTTL} } func (m *Manager) AccessTTL() time.Duration { return m.accessTTL } func (m *Manager) RefreshTTL() time.Duration { return m.refreshTTL } // Sign 签发 token;typ 取 "access" / "refresh"。 func (m *Manager) Sign(userID uint64, username, role, typ string) (string, time.Time, error) { ttl := m.accessTTL if typ == "refresh" { ttl = m.refreshTTL } now := time.Now() exp := now.Add(ttl) claims := Claims{ UserID: userID, Username: username, Role: role, RegisteredClaims: jwt.RegisteredClaims{ Issuer: m.issuer, Subject: typ, IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(exp), }, } tok := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) s, err := tok.SignedString(m.secret) return s, exp, err } var ErrInvalidToken = errors.New("invalid token") // Parse 校验签名与有效期。 func (m *Manager) Parse(token string) (*Claims, error) { claims := &Claims{} tok, err := jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, ErrInvalidToken } return m.secret, nil }) if err != nil || !tok.Valid { return nil, ErrInvalidToken } return claims, nil }