Keycloak 26如何永久存储无效登录尝试记录?
问题分析
你当前使用Keycloak 26.3.0的暴力检测功能时,失败登录次数默认存储在Infinispan缓存中,缓存过期后计数器会自动重置;而永久锁定是直接写入数据库用户实体(user_entity表的enabled字段设为false),因此两者行为不一致。
内置方案:调整缓存有效期(非持久化)
Keycloak没有内置直接将失败次数持久化到数据库的配置,但可以通过调整缓存过期规则避免计数器过早重置:
- 找到Keycloak的
standalone.xml(或domain.xml)配置文件,定位到infinispan子系统中名为bruteForce的缓存容器。 - 修改其缓存的
expiration配置,将max-idle设为极大值,或直接禁用过期:
<cache-container name="bruteForce" default-cache="authFailures"> <local-cache name="authFailures"> <expiration max-idle="-1"/> <!-- -1表示禁用缓存过期 --> </local-cache> </cache-container>
注意:这种方法只是延长缓存有效期,并非真正持久化到数据库,若Keycloak重启或缓存集群出现异常,计数器仍可能丢失。
持久化到数据库的方案:自定义BruteForceProtector SPI
若要将失败次数永久存储到数据库,必须自定义SPI实现:
- 编写自定义BruteForceProtector类
继承DefaultBruteForceProtector,重写处理失败次数的核心方法(incrementFailureCount、resetFailureCount、getFailureCount),将操作直接对接数据库。可使用Keycloak提供的JpaTransactionManager或JDBC完成数据库操作,核心逻辑示例:
@Override public void incrementFailureCount(RealmModel realm, UserModel user) { // 从数据库读取当前失败次数 int currentCount = getFailureCountFromDB(realm.getId(), user.getId()); // 更新数据库中失败次数 updateFailureCountToDB(realm.getId(), user.getId(), currentCount + 1); // 可选:保留缓存同步以提升性能 super.incrementFailureCount(realm, user); }
- 注册SPI
在项目的META-INF/services/org.keycloak.services.managers.BruteForceProtector文件中,添加自定义类的全限定名。 - 打包部署
将自定义扩展打包为JAR,放入Keycloak的providers目录,重启Keycloak后在管理控制台启用该SPI。 - 扩展数据库表(可选)
若Keycloak默认表无存储失败次数的字段,可新增user_login_failures表,关联user_entity表的id字段,存储用户ID、领域ID、失败次数、最后失败时间等信息。
额外注意事项
- 自定义SPI时需处理事务一致性,避免缓存与数据库数据不一致。
- Direct Grant模式下,默认
DefaultBruteForceProtector已支持失败次数统计,重写对应方法即可覆盖该流程。 - 测试时需验证:失败次数成功写入数据库、缓存过期后计数器不丢失、永久锁定逻辑不受影响。
内容的提问来源于stack exchange,提问作者Ipay Test
相关产品推荐
相关产品推荐

