找回密码
 立即注册
首页 业界区 安全 keycloak~分布式部署中会话过期清理机制

keycloak~分布式部署中会话过期清理机制

昝梓菱 3 天前
Keycloak 分布式部署中会话过期清理机制

在 Keycloak 分布式部署(使用外部独立部署的 Infinispan)的架构下,sessions 和 clientSessions 的过期清理涉及两种不同的部署模式,机制略有不同:
架构模式 1:Embedded + Remote Store(嵌入式缓存 + 远程存储)

这种模式下,Keycloak 节点有本地嵌入式缓存,同时配置了远程存储(Remote Store)连接到外部 Infinispan 集群。
  1. /**
  2. * @author Marek Posolda
  3. */
  4. @ClientListener
  5. public class RemoteCacheSessionListener<K, V extends SessionEntity>  {
复制代码
清理机制:

  • 本地缓存自动过期

    • 当会话数据写入本地嵌入式缓存(DefaultSegmentedDataContainer)时,会同时设置 lifespan 和 maxIdle 参数
    • Infinispan 的内置过期机制会自动清除过期条目

1.png


  • 远程缓存事件同步

    • RemoteCacheSessionListener 通过 Hot Rod Client Listener 机制监听远程缓存事件
    • 当远程 Infinispan 中条目被删除时,会触发 @ClientCacheEntryRemoved 事件:

  1.     @ClientCacheEntryRemoved
  2.     public void removed(ClientCacheEntryRemovedEvent event) {
  3.         K key = (K) event.getKey();
  4.         if (shouldUpdateLocalCache(event.getType(), key, event.isCommandRetried())) {
  5.             this.executor.submit(event, () -> {
  6.                 // We received event from remoteCache, so we won't update it back
  7.                 cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_STORE, Flag.SKIP_CACHE_LOAD, Flag.IGNORE_RETURN_VALUES)
  8.                         .remove(key);
  9.             });
  10.         }
  11.     }
复制代码

  • 重要限制

    • Infinispan 不发送过期事件(Expiration Event)给 Hot Rod 客户端监听器!
    • 远程 Infinispan 中的条目过期时,不会主动通知 Keycloak 节点
    • 本地缓存的清理完全依赖于本地 Infinispan 的自动过期机制

架构模式 2:Remote Only(纯远程模式)

这种模式下,Keycloak 不维护本地会话缓存,所有会话数据都直接存储在外部 Infinispan 集群中。
  1.     @Override
  2.     public void removeAllExpired() {
  3.         //rely on Infinispan expiration
  4.     }
  5.     @Override
  6.     public void removeExpired(RealmModel realm) {
  7.         //rely on Infinispan expiration
  8.     }
复制代码
清理机制

  • 完全依赖远程 Infinispan 的过期机制
  • Keycloak 本地 JVM 中没有 DefaultSegmentedDataContainer,因为不使用嵌入式缓存
  • 所有读取操作直接访问远程缓存,过期数据自然不会被读取到
关键代码:过期时间计算

无论哪种模式,会话的过期时间都通过 SessionTimeouts 计算:
  1.     public static long getUserSessionLifespanMs(RealmModel realm, ClientModel client, UserSessionEntity userSessionEntity) {
  2.         return getUserSessionLifespanMs(realm, false, userSessionEntity.isRememberMe(), userSessionEntity.getStarted());
  3.     }
  4.     public static long getUserSessionLifespanMs(RealmModel realm, boolean offline, boolean rememberMe, int started) {
  5.         long lifespan = SessionExpirationUtils.calculateUserSessionMaxLifespanTimestamp(offline, rememberMe,
  6.                 TimeUnit.SECONDS.toMillis(started), realm);
  7.         if (offline && lifespan == IMMORTAL_FLAG) {
  8.             return IMMORTAL_FLAG;
  9.         }
  10.         lifespan = lifespan - Time.currentTimeMillis();
  11.         if (lifespan <= 0) {
  12.             return ENTRY_EXPIRED_FLAG;
  13.         }
  14.         return lifespan;
  15.     }
复制代码
  1.     @Override
  2.     public void removeAllExpired() {
  3.         //rely on Infinispan expiration
  4.     }
  5.     @Override
  6.     public void removeExpired(RealmModel realm) {
  7.         //rely on Infinispan expiration
  8.     }
复制代码
[code]    public static long getUserSessionLifespanMs(RealmModel realm, ClientModel client, UserSessionEntity userSessionEntity) {        return getUserSessionLifespanMs(realm, false, userSessionEntity.isRememberMe(), userSessionEntity.getStarted());    }    public static long getUserSessionLifespanMs(RealmModel realm, boolean offline, boolean rememberMe, int started) {        long lifespan = SessionExpirationUtils.calculateUserSessionMaxLifespanTimestamp(offline, rememberMe,                TimeUnit.SECONDS.toMillis(started), realm);        if (offline && lifespan == IMMORTAL_FLAG) {            return IMMORTAL_FLAG;        }        lifespan = lifespan - Time.currentTimeMillis();        if (lifespan

相关推荐

您需要登录后才可以回帖 登录 | 立即注册