使用 Keycloak 25 保持用户登录状态

2024 年 6 月 12 日,作者:Alexander Schwartz

先前版本的 Keycloak 仅在内存中存储常规用户会话(也称为在线用户会话)。因此,当您关闭或重启 Keycloak 集群时,所有用户都将被登出。

在 Keycloak 25 中,有一个预览功能“持久用户会话”,它将用户会话存储在其数据库中。如果在内存中找不到会话,则会从数据库加载,用户可以继续使用他们的会话,而无需重新身份验证。

此预览功能默认禁用,您需要使用 persistent-user-sessions 功能标志启用它才能试用。

您可以通过在此 GitHub 讨论帖中提供反馈来帮助使此功能得到完全支持。在 6 月 24 日,我们计划举办一个关于持久会话的“有问必答”环节

Keycloak 和数据库的运行时行为已更改

启用此功能后,Keycloak 的内存使用量可能会减少,而数据库使用量可能会增加。

  • 如果在 Keycloak 的缓存配置 XML 文件中未配置其他最大大小,则 Keycloak 将默认为 sessions、clientSessions、offlineSessions 和 offlineClientSessions 这些缓存中的每一个设置最大 10,000 个条目。如果您想在内存中保留更多会话,请参阅配置分布式缓存,了解如何配置不同的大小。

  • 选项 spi-user-sessions-infinispan-offline-session-cache-entry-lifespan-overridespi-user-sessions-infinispan-offline-client-session-cache-entry-lifespan-override 将被忽略,因为将改为使用最大条目大小。

  • 外部 Infinispan 实例支持 Keycloak 的多站点设置。如果您使用此类设置并启用了持久用户会话,则可以(并且应该)设置要在外部 Infinispan 中保留的最大会话数,以限制外部 Infinispan 的内存消耗。请参阅 Infinispan 关于如何在 Infinispan 缓存中配置驱逐的文档。

  • 如果并发用户会话数超过 Keycloak 中的最大缓存大小,您将看到数据库活动增加,以便在例如令牌刷新或调用用户信息端点时从数据库加载会话。这些请求也会导致延迟增加,具体取决于数据库对这些读取语句的响应时间。监控缓存命中率,以查看您的设置是否需要优化。

  • 对于每次登录、令牌刷新和注销,数据库中的会话表都会更新,并将显示为数据库活动增加。Keycloak 尝试将并发会话更新捆绑到单个事务中,但数据库的 CPU 和 IOPS 利用率仍将显着增加。这些请求也会导致延迟增加,具体取决于数据库对这些写入语句的响应时间。

对您环境的影响将取决于您的基础设施和使用模式。作为参考指标,我们使用以下设置运行了一项测试

  • 每秒 150 次登录和 150 次注销

  • Aurora PostgreSQL 区域数据库 15.5

  • 类型 db.t4g.large 服务器(2 个 ARM vCPU 核心,8 GB RAM)

我们观察到运行时指标的以下变化

  • 在数据库方面

    • 每秒额外 300 次提交

    • CPU 使用率增加 1 到 1.5 个 CPU 核心,具体取决于并发会话数

    • 大约额外 2500 个 WriteIOPS

  • 在 Keycloak 方面

    • Keycloak 上的 CPU 使用率保持恒定

    • 创建 10,000 个会话后,内存使用率保持恒定

    • 对于单可用区数据库,登录和注销的第 50 百分位响应时间分别增加了 20 毫秒和 10 毫秒;对于双可用区数据库,则分别增加了 30 毫秒和 20 毫秒。

我们建议您为您的环境运行基准测试。使用我们在 Keycloak Benchmark Project 中提供的工具作为工具箱。

请参阅启用 Keycloak 指标,了解如何为 Keycloak 启用指标以监控有关您的缓存和 HTTP 响应时间的信息。

从先前的社区解决方案迁移

社区过去一直在评估不同的配置,其中一些配置存在缺点,并且未获得 Keycloak 的官方支持。启用持久会话后,现在可以简化这些设置。

使用具有非常大的 JVM 堆大小的部署:过去,需要大量 JVM 内存来将所有会话保存在内存中,并避免内存不足的情况。由于持久会话存储在数据库中,并且仅在内存中保留一个子集用于缓存,因此您现在可以减少分配给 Keycloak 实例的内存。

使用离线会话来保持用户登录状态

一种流行的方法是使用离线会话来保持用户登录状态,因为即使在此之前,这些会话也已持久保存在数据库中。但是,离线会话的用途不同:其预期用途是允许应用程序代表用户访问资源,即使该用户已注销,并且常规在线会话注销也不会注销这些会话。启用持久用户会话后,您应该开始使用在线会话。现有的离线会话仍然可以使用,并且最终会过期。

将 JDBC 存储连接到 Keycloak 的嵌入式 Infinispan

在此设置中,嵌入式 Infinispan 将会话存储到数据库和自定义创建的表中。虽然这是登录和注销的默认设置,但只有在启动时加载所有会话时才会这样做,因为非持久用户会话的代码假定所有会话都位于内存中。所有会话都需要在启动时加载,否则客户端或 realm 的会话列表将不完整,并且无法保证例如给定用户只有一个会话的约束。借助 Keycloak 25 中的持久会话预览功能,这种新方法降低了设置的复杂性,并减少了 Keycloak 和 Infinispan 的内存占用。请参阅下文,了解如何迁移现有会话。

将 Keycloak 连接到用于单站点设置的外部 Infinispan

在此设置中,Keycloak 将读取会话并将其写入外部 Infinispan。与上述情况类似,所有会话都需要在启动时加载到嵌入式 Infinispan 和外部 Infinispan 中,否则客户端或 realm 的会话列表将不完整,并且无法保证例如给定用户只有一个会话的约束。从 Keycloak 24 开始,此类设置仅支持多站点设置。借助 Keycloak 25 中的持久会话预览功能,这种新方法降低了设置的复杂性,并减少了 Keycloak 的内存占用,并且无需运行外部 Infinispan。请参阅下文,了解如何迁移现有会话。

迁移现有会话

如果您一直在使用连接到嵌入式 Infinispan 的 JDBC 存储,或外部 Infinispan 在 Keycloak 24 中存储 Keycloak 在线会话,则当您首次启动 Keycloak 25 时启用持久用户会话(当且仅当启用时),您可以迁移这些会话。

迁移完成后,您应该删除嵌入式会话缓存的任何 JDBC 持久性配置。如果您在单站点设置中使用过外部 Infinispan,您还应该删除与外部 Infinispan 的连接。

启用持久用户会话

由于这是一个预览功能,因此默认情况下未启用。一旦我们认为此功能已完全支持,我们计划在未来的版本中默认启用它。

如果您已迁移到 Keycloak 25,我们建议您清除设置中的所有现有在线用户会话。

根据您是在开发环境中使用它、构建 Keycloak 发行版,还是依赖于启动时自动重建 Keycloak,您的命令将如下所示

bin/kc.[sh|bat] [start-dev|build|start] --features="persistent-user-sessions"

如果您使用环境变量来设置选项,请设置以下环境变量,或者如果环境变量已存在,则添加该值。

KC_FEATURES=persistent-user-sessions

如果您使用 Keycloak Operator,请将其添加到 Keycloak CR 中启用的功能中

apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
  name: example-kc
spec:
  features:
    enabled:
      - persistent-user-sessions
...

请参阅启用和禁用功能,了解有关如何启用功能的更多信息。

展望

在我们努力使此功能获得完全支持的同时,我们也在开发类似的功能。其中一些功能将使 Keycloak 的部署更简单,而另一些功能最终将实现 Keycloak 多站点主动-主动设置。

加入这些功能的讨论,并为它们投赞成票,以便我们了解您对此感兴趣。

致谢、提供反馈和提问

感谢 Keycloak 团队成员 Kamesh Akella、Michal Hajas、Pedro Ruivo、Anna Manukyan 和 Ryan Emerson,他们讨论了想法和边缘情况,贡献了代码并执行了中间拉取请求和版本的测试。特别感谢社区成员 Tristan971、daviddelannoy 和 Thomas Darimont,他们加入了 GitHub 讨论并提供了反馈。

您可以通过试用预览功能并在此 GitHub 讨论帖中提供反馈来帮助使此功能获得完全支持。

也可以使用此帖子询问有关持久用户会话的问题。在 6 月 24 日,我们计划举办一个关于持久会话的“有问必答”环节