如何在Keycloak中限制用户访问令牌数量?(Spring Boot场景)
限制Keycloak用户活跃JWT令牌数量的最佳实现方案
1. 切换Spring Security至令牌自省模式
Spring Security默认对JWT采用本地验证(直接校验签名和Claims),不会触发Keycloak的自省端点。要改为调用自省验证,需配置为**不透明令牌(Opaque Token)**模式,Keycloak的自省端点完全兼容OAuth2的令牌自省规范。
配置步骤:
- 在
application.yml中配置自省相关参数:
spring: security: oauth2: resourceserver: opaque-token: introspection-uri: http://your-keycloak-server/auth/realms/your-realm/protocol/openid-connect/token/introspect client-id: your-resource-server-client-id client-secret: your-resource-server-client-secret
- 在SecurityFilterChain中启用opaqueToken验证:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .oauth2ResourceServer(oauth2 -> oauth2 .opaqueToken(opaque -> opaque.introspector(customOpaqueTokenIntrospector()))); return http.build(); }
2. 实现令牌黑名单存储
要限制活跃令牌数量,需在用户获取新令牌时,将同用户的旧令牌加入黑名单。推荐用Redis作为黑名单存储(支持快速查询和自动过期):
登录/令牌刷新时的黑名单逻辑:
- 方式一:自定义Keycloak事件监听器(SPI)
编写Keycloak的EventListenerProvider,监听用户的LOGIN或REFRESH_TOKEN事件,当用户获取新令牌时,从Keycloak的会话中取出该用户的旧令牌,存入Redis黑名单,并设置过期时间与令牌的exp字段一致。 - 方式二:客户端侧拦截令牌请求
如果是你的客户端调用Keycloak获取令牌,在拿到新令牌后,调用自定义接口将用户的旧令牌(可从客户端缓存中获取)存入Redis黑名单。
Redis操作示例:
@Autowired private StringRedisTemplate redisTemplate; // 将令牌加入黑名单 public void addToBlacklist(String token, long expireTime) { redisTemplate.opsForValue().set("token:blacklist:" + token, "invalid", expireTime, TimeUnit.SECONDS); } // 检查令牌是否在黑名单中 public boolean isBlacklisted(String token) { return Boolean.TRUE.equals(redisTemplate.hasKey("token:blacklist:" + token)); }
3. 在自省流程中加入黑名单校验
自定义OpaqueTokenIntrospector,在Keycloak自省验证通过后,额外检查令牌是否在黑名单中:
@Component public class CustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector { private final OpaqueTokenIntrospector delegate; private final TokenBlacklistService blacklistService; public CustomOpaqueTokenIntrospector(TokenBlacklistService blacklistService) { this.delegate = new NimbusOpaqueTokenIntrospector( "http://your-keycloak-server/auth/realms/your-realm/protocol/openid-connect/token/introspect", "your-resource-server-client-id", "your-resource-server-client-secret" ); this.blacklistService = blacklistService; } @Override public OAuth2AuthenticatedPrincipal introspect(String token) { // 先调用Keycloak自省端点验证令牌有效性 OAuth2AuthenticatedPrincipal principal = delegate.introspect(token); // 检查令牌是否在黑名单中 if (blacklistService.isBlacklisted(token)) { throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, "Token is blacklisted", null)); } return principal; } }
4. 可选:Keycloak会话限制配合
在Keycloak的Realm配置中,进入Session Settings,设置Maximum Sessions Per User为你想要限制的数量。Keycloak会自动失效用户的旧会话,但JWT本身是无状态的,已颁发的令牌仍会在有效期内有效,因此需要配合上述黑名单+自省的机制,才能彻底限制活跃令牌。
内容的提问来源于stack exchange,提问作者Rasool Ghafari
相关产品推荐
相关产品推荐

