Keycloak使用账号密码登录后如何撤销旧的refresh token?
Keycloak 新登录后撤销旧Refresh Token实现方案
基础前置配置
首先开启Refresh Token单次有效配置,满足你提到的refresh token被盗用后用户能感知的需求:
- 进入对应Realm的
Clients页面,找到你正在使用的客户端,进入Settings标签 - 找到
OpenID Connect Compatibility Modes模块,开启Revoke Refresh Token选项,该配置就是你提到的reuse refresh=0效果,Refresh Token使用一次后会自动失效,用过期的Refresh Token请求刷新会直接返回400错误 - 根据业务需求调整
Access Token Lifespan、Refresh Token Lifespan的时长,建议Refresh Token最长有效期不超过7天
新密码登录自动撤销旧Refresh Token实现
方案1:内置会话并发控制(无代码侵入,推荐)
Keycloak自带会话数量限制能力,直接配置即可实现需求:
- 进入对应Realm的
Realm Settings→Sessions标签页 - 将
Session count limit配置为1,表示同一用户同一时间仅允许存在1个有效会话 When session limit is exceeded选项选择Terminate oldest session,即新会话创建后自动终止最老的会话- 保存配置后,用户每次用账号密码完成登录生成新会话时,旧会话关联的所有Refresh Token、Access Token都会被自动撤销,完全符合需求
注意:该配置为Realm全局生效,会作用于该Realm下的所有客户端,若需要仅针对单个客户端生效,可使用下方自定义SPI方案
方案2:自定义认证SPI(灵活可控)
如果需要细粒度控制(比如仅针对密码授权模式触发旧Token撤销),可以自行扩展Keycloak认证逻辑:
- 实现
Authenticator接口,在密码认证成功的逻辑后添加如下核心代码:
// 获取当前用户所有有效会话 List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user); for (UserSessionModel userSession : userSessions) { // 跳过当前新生成的会话 if (userSession.getId().equals(context.getAuthenticationSession().getParentSession().getId())) { continue; } // 移除旧会话,关联的所有Token会自动失效 session.sessions().removeUserSession(realm, userSession); }
- 将自定义SPI打包后放入Keycloak安装目录的
providers文件夹,重启Keycloak生效 - 进入Realm的
Authentication→Flows页面,找到Direct Grant(密码授权对应的认证流),将自定义的认证步骤添加到密码校验步骤之后即可
效果验证
配置完成后可按以下步骤测试是否生效:
- 第一次用账号密码请求Token,得到
refresh_token_1和access_token_1,可正常使用刷新接口获取新Token,刷新后refresh_token_1自动失效 - 第二次用同一个账号密码请求Token,得到
refresh_token_2和access_token_2 - 用之前生成的所有旧Refresh Token请求刷新,都会返回
invalid_grant错误,说明旧Token已被成功撤销
内容的提问来源于stack exchange,提问作者qwerty
相关产品推荐
相关产品推荐

