Skip to content

令牌黑名单

黑名单用于在令牌过期前主动使其失效,适用于用户登出、密码修改、权限变更等场景。

工作原理

text
Revoke(token) → 提取 jti + exp → 写入 BlacklistStore
Validate(token) → 验证签名 → 检查黑名单 → 返回结果

内置内存存储

默认使用内存存储,开箱即用:

go
cfg := jwt.DefaultConfig()
cfg.SecretKey = "hmac-key-that-has-at-least-32-bytes!"
// 黑名单已自动启用,使用 DefaultBlacklistConfig()

配置项

go
cfg.Blacklist.CleanupInterval = 5 * time.Minute  // 清理间隔
cfg.Blacklist.MaxSize = 100000                     // 最大条目数
cfg.Blacklist.EnableAutoCleanup = true             // 自动清理
字段默认值说明
CleanupInterval5m过期条目清理间隔
MaxSize100000最大条目数
EnableAutoCleanuptrue自动清理(强制为 true)

自动清理

内置存储的 EnableAutoCleanup 始终强制为 true,防止内存无限增长。

吊销令牌

go
// 吊销
err := processor.Revoke(accessToken)
if err != nil {
    panic(err)
}

// 检查
revoked, err := processor.IsRevoked(accessToken)
fmt.Println("Revoked:", revoked) // true

// 已吊销的令牌验证会失败
_, _, err = processor.Validate(accessToken)
// err → jwt.ErrTokenRevoked

自定义存储后端

实现 BlacklistStore 接口对接外部存储(Redis、数据库等):

go
type BlacklistStore interface {
    Add(tokenID string, expiresAt time.Time) error
    Contains(tokenID string) (bool, error)
    Close() error
}

Redis 示例

go
type RedisStore struct {
    client *redis.Client
}

func (s *RedisStore) Add(tokenID string, expiresAt time.Time) error {
    ttl := time.Until(expiresAt)
    if ttl <= 0 {
        return nil // 已过期的令牌无需存储
    }
    return s.client.Set(ctx, "blacklist:"+tokenID, "1", ttl).Err()
}

func (s *RedisStore) Contains(tokenID string) (bool, error) {
    n, err := s.client.Exists(ctx, "blacklist:"+tokenID).Result()
    return n > 0, err
}

func (s *RedisStore) Close() error {
    return s.client.Close()
}

使用自定义存储:

go
cfg.Blacklist.Store = &RedisStore{client: rdb}

TTL 优化

使用 time.Until(expiresAt) 作为 Redis TTL,令牌过期后自动从黑名单中移除,无需额外清理。

生产环境建议

注意事项

  • 内置内存存储不跨进程共享,多实例部署时需使用外部存储
  • MaxSize 达到上限后,新吊销的令牌会挤掉最早的条目
  • 自定义存储实现应处理网络超时和重试

下一步