65 lines
1.8 KiB
Go
65 lines
1.8 KiB
Go
package middleware
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
// tokenBlacklist 登出令牌黑名单(内存版,进程重启后失效,令牌自然过期兜底)
|
|
type tokenBlacklist struct {
|
|
mu sync.RWMutex
|
|
revoked map[string]time.Time // tokenID(jti) -> 过期时间
|
|
}
|
|
|
|
var defaultBlacklist = &tokenBlacklist{revoked: make(map[string]time.Time)}
|
|
|
|
// RevokeToken 将令牌加入黑名单(按 jti,若无 jti 则按 subject+签发时间)
|
|
func RevokeToken(claims *JWTClaims, tokenStr string, exp time.Time) {
|
|
id := tokenIdentifier(claims)
|
|
defaultBlacklist.mu.Lock()
|
|
defaultBlacklist.revoked[id] = exp
|
|
defaultBlacklist.mu.Unlock()
|
|
}
|
|
|
|
// IsRevoked 判断令牌是否已被吊销
|
|
func IsRevoked(claims *JWTClaims) bool {
|
|
id := tokenIdentifier(claims)
|
|
defaultBlacklist.mu.RLock()
|
|
exp, ok := defaultBlacklist.revoked[id]
|
|
defaultBlacklist.mu.RUnlock()
|
|
if !ok {
|
|
return false
|
|
}
|
|
// 已过期的黑名单项自动清理
|
|
if time.Now().After(exp) {
|
|
defaultBlacklist.mu.Lock()
|
|
delete(defaultBlacklist.revoked, id)
|
|
defaultBlacklist.mu.Unlock()
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// ExtractClaims 从 token 字符串解析 claims(供 logout handler 使用)
|
|
func ExtractClaims(tokenStr string, secret string) (*JWTClaims, *jwt.Token, error) {
|
|
claims := &JWTClaims{}
|
|
token, err := jwt.ParseWithClaims(tokenStr, claims, func(t *jwt.Token) (interface{}, error) {
|
|
return []byte(secret), nil
|
|
}, jwt.WithValidMethods([]string{"HS256"}))
|
|
return claims, token, err
|
|
}
|
|
|
|
// tokenIdentifier 返回令牌唯一标识:优先 jti,否则用 subject+签发时间
|
|
func tokenIdentifier(claims *JWTClaims) string {
|
|
if claims.ID != "" {
|
|
return claims.ID
|
|
}
|
|
iat := ""
|
|
if claims.IssuedAt != nil {
|
|
iat = claims.IssuedAt.Format(time.RFC3339Nano)
|
|
}
|
|
return claims.Subject + "|" + iat
|
|
}
|