You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 08:15:26