Spring Boot中使用keycloak-admin-client启用Keycloak邮箱2FA
Keycloak 邮箱2FA实现方案(keycloak-admin-client版)
前置配置(Keycloak侧)
- 进入对应Realm的Authentication页面,复制默认Browser认证流程,重命名为自定义流程(例如
browser-email-2fa) - 在自定义流程的密码认证步骤之后,添加内置的
Email OTP认证执行器,将执行器触发要求设为REQUIRED,移除/禁用同位置的TOTP、WebAuthn等其他二次认证执行器避免冲突 - 完成Realm级SMTP服务配置,发送测试邮件验证收发正常,否则无法下发OTP令牌
- 给调用Admin API的服务账号分配对应权限:需要
view-users、manage-users角色,否则操作用户凭据时会返回403 - 将修改完成的
browser-email-2fa流程绑定到业务系统对应的Keycloak客户端,设置为该客户端的浏览器登录默认认证流程,否则2FA步骤不会触发
核心代码实现
1. 为用户启用邮箱2FA
Keycloak 18及以上版本内置邮箱OTP能力,不需要自行开发OTP生成、邮件发送、校验逻辑,直接通过admin-client操作用户凭据即可:
import org.keycloak.admin.client.Keycloak; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.UserRepresentation; import java.util.ArrayList; import java.util.List; import java.util.Map; // 提前初始化完成的Keycloak admin客户端实例 private final Keycloak keycloak; public void enableUserEmail2fa(String realm, String userId) { // 前置校验:用户必须绑定已验证的邮箱 UserRepresentation user = keycloak.realm(realm).users().get(userId).toRepresentation(); if (user.getEmail() == null || !Boolean.TRUE.equals(user.isEmailVerified())) { throw new RuntimeException("请先完成邮箱绑定及验证后再启用二次认证"); } // 写入邮箱OTP凭据配置 var credentialManager = keycloak.realm(realm) .users() .get(userId) .credentialManager(); CredentialRepresentation emailOtpCred = new CredentialRepresentation(); emailOtpCred.setType("email-otp"); emailOtpCred.setUserLabel("邮箱二次验证"); // 自定义OTP规则:有效期5分钟、6位数字、单次使用即失效 Map<String, List<String>> otpConfig = Map.of( "otp.validity.period", List.of("300"), "otp.length", List.of("6"), "otp.single.use", List.of("true") ); emailOtpCred.setConfig(otpConfig); credentialManager.createCredential(emailOtpCred); // 标记用户首次使用需要完成OTP验证,验证通过后Keycloak会自动移除该标记 List<String> requiredActions = user.getRequiredActions() == null ? new ArrayList<>() : new ArrayList<>(user.getRequiredActions()); if (!requiredActions.contains("CONFIGURE_EMAIL_OTP")) { requiredActions.add("CONFIGURE_EMAIL_OTP"); } user.setRequiredActions(requiredActions); keycloak.realm(realm).users().get(userId).update(user); }
2. 为用户禁用邮箱2FA
直接删除用户名下的邮箱OTP凭据、移除对应强制操作即可:
public void disableUserEmail2fa(String realm, String userId) { var credentialManager = keycloak.realm(realm) .users() .get(userId) .credentialManager(); // 清除用户所有邮箱OTP类型的凭据 List<CredentialRepresentation> userCreds = credentialManager.getCredentials(); userCreds.stream() .filter(cred -> "email-otp".equals(cred.getType())) .forEach(cred -> credentialManager.removeCredential(cred.getId())); // 移除对应的强制操作标记 UserRepresentation user = keycloak.realm(realm).users().get(userId).toRepresentation(); List<String> requiredActions = user.getRequiredActions() == null ? new ArrayList<>() : new ArrayList<>(user.getRequiredActions()); requiredActions.remove("CONFIGURE_EMAIL_OTP"); user.setRequiredActions(requiredActions); keycloak.realm(realm).users().get(userId).update(user); }
3. 自定义UI对接注意事项
- 2FA开关状态查询:直接拉取用户凭据列表,判断是否存在
email-otp类型的有效凭据即可,不需要额外在业务库存储状态 - 登录流程适配:用户完成密码校验后,Keycloak会自动向用户绑定邮箱发送OTP,你只需要将Keycloak认证流程返回的OTP输入节点和自定义UI做映射,用户输入的验证码直接回传给Keycloak认证接口做校验即可,不需要自行实现校验逻辑
- 安全规则复用:OTP最大错误尝试次数、用户锁定时长等规则直接在Keycloak的Email OTP执行器配置页修改即可,会自动复用Keycloak原有安全防护逻辑
- 邮箱变更适配:如果用户修改绑定邮箱,需要先清除原有邮箱OTP凭据,提醒用户重新完成2FA配置,避免令牌下发到旧邮箱
常见踩坑
- 不要使用18以下版本Keycloak:低版本没有内置Email OTP执行器,需要自行安装第三方扩展,维护成本高
- SMTP配置必须开启TLS:大部分公共邮箱服务商默认拒绝未加密的邮件发送请求
- 不要尝试自行实现OTP生成、存储、校验逻辑:所有认证逻辑交给Keycloak原生能力处理,避免出现安全漏洞
内容的提问来源于stack exchange,提问作者abishkar bhattarai
相关产品推荐
相关产品推荐

