后端认证鉴权单点登录【免费下载链接】casApereo CAS - Identity Single Sign On for all earthlings and beyond.项目地址https://gitcode.com/gh_mirrors/ca/cas点击查看免费下载导读本文围绕 Apereo CAS 的Passwordless Authentication免密认证功能展开聚焦如何将 Duo Security 配置为 CAS 的免密账户存储Passwordless Account Store。阅读完本文你将掌握Duo 免密账户存储的启用前提与配置方法、CAS 如何通过 Duo Admin API 判定用户是否具备免密资格、符合条件的用户在收到 Duo 推送通知后免密登录的完整流程以及不合格账户如何优雅回退到常规密码less流程。本文所有实现细节均以当前仓库Apereo CAS源码与测试为据可直接对照查阅。一、功能定位Duo Security 不只是 MFA 提供商在 CAS 的生态中Duo Security 最常见的角色是多因素认证MFA提供商参见 Duo Security 认证文档。但除此之外Duo Security也可以充当一个由 CAS 直接持有的免密账户存储当用户在 Duo Security 侧注册了有效的邮箱与手机/移动设备后CAS 可以通过 Duo 的推送通知完成身份验证用户全程无需输入密码。需要特别强调的是该能力与 Duo Security 自身的 Passwordless / PassKey 能力无关也与 CAS 提供的 PassKey/WebAuthn 免密 MFA 能力无关。配置属性的源码注释对此做了明确说明When enabled, this option allows CAS to use Duo Security as a CAS-owned passwordless authentication provider and account store. Note that this has nothing to do with Duo Securitys Passwordless/PassKey capabilities, or PassKey/WebAuthn capabilities of CAS... This solely controls the passwordless authentication feature that is provided by CAS directly.即本文讨论的是 CAS 自带的免密认证功能Duo 只是这个功能的账户数据来源与推送载体。二、工作原理与整体流程从 DuoSecurityPasswordlessUserAccountStore 的源码可以还原出完整的免密判定链路用户在 CAS 登录页输入用户名触发 CAS 的免密认证流程免密账户存储查找器PasswordlessUserAccountStore被调用遍历当前应用上下文中所有DuoSecurityMultifactorAuthenticationProvider只保留开启了passwordlessAuthenticationEnabled的 Duo 提供商通过 DuoAdmin API查询该用户账户信息getDuoSecurityUserAccount(username, false)若 Duo 中不存在该用户或用户状态为ENROLL未注册则该用户不具备免密资格CAS 回退到常规的 CAS 免密认证流程如邮箱/短信验证码等若用户存在且状态合格CAS 将用户映射为PasswordlessUserAccount随后进入免密验证阶段——由 DuoSecurityVerifyPasswordlessAuthenticationAction 构造DuoSecurityDirectCredential并调用 Duo 发起推送用户点击批准后即完成登录。整个链路中CAS 在【免密账户存储查询】和【推送认证】两个环节分别与 Duo 交互前者走 Admin API后者走常规的 Duo 认证 API。三、启用前置条件要将 Duo 配置为免密账户存储需要同时满足以下条件条件说明CAS 已启用 Passwordless 认证该功能要求 CAS 已经具备免密认证能力即PasswordlessAuthn特性开启CAS 已配置 Duo MFA 提供商需要在cas.authn.mfa.duo下配置至少一个 Duo 集成开启免密账户存储开关将对应 Duo 提供商的passwordless-authentication-enabled设为true配置 Duo Admin API 凭据需要duo-admin-integration-key与duo-admin-secret-key用于查询用户账户状态Duo 集成类型与权限需要一种允许 CAS 调用 Admin API 并返回用户账户详情的集成类型错误类型会导致 API 报错并在日志中告警此时 CAS 会忽略账户状态继续走认证流程用户侧数据完备用户必须在 Duo 中注册了有效的邮箱和手机/移动设备否则不具备推送免密条件源码中功能启用通过双特性条件控制见 DuoSecurityPasswordlessAuthenticationConfigurationConditionalOnFeaturesEnabled({ ConditionalOnFeatureEnabled(feature CasFeatureModule.FeatureCatalog.PasswordlessAuthn), ConditionalOnFeatureEnabled(feature CasFeatureModule.FeatureCatalog.MultifactorAuthentication, module duo) }) ConditionalOnClass(PasswordlessUserAccountStore.class)也就是说只有同时满足免密认证特性开启与Duo MFA 特性开启两个条件且 classpath 中存在PasswordlessUserAccountStore时该自动配置才会生效。该配置类由 CasDuoSecurityAutoConfiguration 统一导入。四、配置示例与参数详解以下配置启用了一个 Duo 提供商并将其同时作为免密账户存储属性键参考自 DuoSecurityPasswordlessUserAccountStoreTests 的测试配置# --- Duo 常规认证配置 --- cas.authn.mfa.duo[0].duo-integration-keyDUO_INTEGRATION_KEY cas.authn.mfa.duo[0].duo-secret-keyDUO_SECRET_KEY cas.authn.mfa.duo[0].duo-api-hosthttps://api-xxxx.duosecurity.com # --- 将 Duo 作为免密账户存储的关键开关 --- cas.authn.mfa.duo[0].passwordless-authentication-enabledtrue # --- Duo Admin API 凭据免密账户查询必需 --- cas.authn.mfa.duo[0].duo-admin-integration-keyDUO_ADMIN_INTEGRATION_KEY cas.authn.mfa.duo[0].duo-admin-secret-keyDUO_ADMIN_SECRET_KEY其中passwordless-authentication-enabled是核心开关其定义位于 DuoSecurityMultifactorAuthenticationProperties配置项类型默认值说明cas.authn.mfa.duo[i].passwordless-authentication-enabledbooleanfalse是否允许 CAS 将本 Duo 提供商作为免密账户存储。开启后 CAS 会调用 Duo 查询免密合格账户cas.authn.mfa.duo[i].duo-admin-integration-keyString无Duo Admin 集成 Key支持 SpEL 表达式ExpressionLanguageCapable用于 Admin API 调用cas.authn.mfa.duo[i].duo-admin-secret-keyString无Duo Admin 密钥支持 SpEL 表达式用于 Admin API 调用cas.authn.mfa.duo[i].account-status-enabledbooleantrue是否让 CAS 联系 Duo 查询用户账户状态以判定 MFA 资格。关闭后用户账户状态直接视为可认证且不会发起 Admin API 调用cas.authn.mfa.duo[i].duo-integration-key/duo-secret-key/duo-api-hostString无Duo 常规认证所需的集成 Key、密钥与 API 主机推送认证依赖此配置cas.authn.mfa.duo[i].collect-duo-attributesbooleantrue是否收集 Duo 返回的认证元数据与用户属性作为 CAS 属性cas.authn.mfa.duo[i].principal-attributeString无用于解析发送给 Duo 的用户名的主属性未定义时使用默认主体 ID注意duo-admin-integration-key与duo-admin-secret-key是免密账户查询的关键凭据。如果缺失DuoSecurityAuthenticationService.getAdminApiService()将无法提供 Admin API 服务返回空免密账户查询环节将无法执行。五、源码级实现剖析5.1 账户查询与资格判定免密账户存储的实现类是DuoSecurityPasswordlessUserAccountStoresince 7.1.0。其核心逻辑位于locatePasswordlessAccount方法protected Optional? extends PasswordlessUserAccount locatePasswordlessAccount(final PasswordlessAuthenticationRequest request) { val providers applicationContext.getBeansOfType(DuoSecurityMultifactorAuthenticationProvider.class).values(); return providers .stream() .filter(Objects::nonNull) .filter(BeanSupplier::isNotProxy) .filter(provider - provider.getDuoAuthenticationService().getProperties().isPasswordlessAuthenticationEnabled()) .map(provider - { val duoService provider.getDuoAuthenticationService(); return duoService .getAdminApiService() .flatMap(admin - FunctionUtils.doUnchecked(() - admin.getDuoSecurityUserAccount(request.getUsername(), false))); }) .filter(Optional::isPresent) .map(Optional::get) .filter(account - account.getStatus() ! DuoSecurityUserAccountStatus.ENROLL) .map(duoAccount - PasswordlessUserAccount .builder() .username(duoAccount.getUsername()) .email(duoAccount.getEmail()) .name(duoAccount.getFirstName()) .phone(duoAccount.getPhone()) .source(duoAccount.getProviderId()) .build()) .findFirst(); }代码逻辑可提炼为以下几点多提供商支持CAS 可能配置了多个 Duo 集成代码遍历所有DuoSecurityMultifactorAuthenticationProvider只选择启用了免密开关的提供商Admin API 查询通过getAdminApiService().flatMap(...)调用getDuoSecurityUserAccount(username, false)查询账户第二个参数false表示不拉取 bypass codes状态过滤查询结果中状态为ENROLL用户未在 Duo 注册的账户被排除不符合免密资格数据映射将 Duo 侧账户的username / email / firstName / phone / providerId分别映射为 CAS 侧PasswordlessUserAccount的username / email / name / phone / source其中source记录该账户来自哪个 Duo 提供商自定义扩展点findUser方法在拿到账户后会依次执行所有PasswordlessUserAccountCustomizer允许对账户对象做二次定制如补充属性且代理 Bean 会被过滤BeanSupplier::isNotProxy。5.2 Admin API 查询的底层实现账户查询最终由DefaultDuoSecurityAdminApiService完成见 DefaultDuoSecurityAdminApiServiceOverride public OptionalDuoSecurityUserAccount getDuoSecurityUserAccount(final String username, final boolean fetchBypassCodes) throws Exception { val userResponse (JSONArray) executeAdminEndpoint(CollectionUtils.wrap(uri, users, username, username)); if (userResponse ! null userResponse.length() 1) { val userJson userResponse.getJSONObject(0); val user mapDuoSecurityUserAccount(userJson); ... return Optional.of(user); } return Optional.empty(); }实际调用的是 Duo Admin API 的users端点/admin/v1/usersgetAdminEndpointUri中拼接了/admin/v1/前缀若按用户名查询不到唯一用户返回为空或数量不为 1则返回Optional.empty()CAS 视为不具备免密资格查到的用户 JSON 会被映射为DuoSecurityUserAccount其中状态通过DuoSecurityUserAccountStatus.from(...)翻译bypass → ALLOW、disabled/locked/pending_deletion → DENY、其他默认AUTH若用户未注册设备则状态会被置为ENROLL见DefaultDuoSecurityAdminApiService中if (user.getStatus() ! DENY !user.isEnrolled()) user.setStatus(ENROLL)的逻辑。5.3 状态枚举语义状态枚举定义于 DuoSecurityUserAccountStatus各状态含义如下状态含义免密资格AUTH用户已知且允许认证✅ 具备ALLOW用户被配置为绕过二次认证bypass✅ 具备DENY用户当前不允许认证disabled/locked/pending_deletion❌ 不具备ENROLL用户未在 Duo 注册需要注册❌ 不具备被显式过滤UNAVAILABLEDuo 服务不可用视情况而定从locatePasswordlessAccount的filter(account - account.getStatus() ! ENROLL)可以看到只有ENROLL状态被免密账户查询显式排除DENY等状态账户在查询阶段仍可返回其认证结果由后续 Duo 认证动作决定。5.4 免密验证动作与推送认证当 CAS 判定用户具备免密资格并完成账户映射后进入验证阶段。DuoSecurityVerifyPasswordlessAuthenticationAction 负责执行推送认证从 Webflow 请求上下文中取出之前存入的PasswordlessUserAccount以账户用户名创建 principal构造DuoSecurityDirectCredential(principal, provider.getId())绑定租户信息若启用多租户见TenantExtractor通过AuthenticationSystemSupport发起并完成初始认证事务将认证结果写入 Webflow 上下文认证成功返回success事件失败返回error事件。该动作同样只对启用了passwordlessAuthenticationEnabled的 Duo 提供商生效且当 Duo 认证服务条件不满足时DuoSecurityAuthenticationService.CONDITION配置类会降级为ConsumerExecutionAction.NONE空操作确保不会影响未启用场景下的常规流程。5.5 测试验证仓库自带的测试 DuoSecurityPasswordlessUserAccountStoreTests 完整覆盖了上述链路以MockWebServer模拟 Duo Admin API返回duo-adminapi-user.json中的用户数据验证passwordlessUserAccountStore.findUser(request)能正确返回映射后的账户用户名jsmith、邮箱jsmithexample.com、source为 Duo 提供商 ID验证duoSecurityVerifyPasswordlessAuthenticationActionBean 在配置下正常装配模拟 Duo 认证 API 返回{stat:OK,response:{result:allow,status:allow}}验证 Webflow 动作返回success事件且认证结果与凭据被正确写入上下文。测试配置中还展示了完整的属性样例passwordless-authentication-enabledtrue、duo-admin-secret-key、duo-admin-integration-key等可作为集成时的参考基线。六、不合格账户的回退行为这是该功能设计中非常关键的一点Duo 账户不符合免密条件并不会阻塞登录而是平滑回退。具体而言用户在 Duo 中不存在Admin API 查询为空→ 视为无免密账户 → 回退常规 CAS 免密流程用户状态为ENROLL未注册→ 被locatePasswordlessAccount过滤 → 回退常规 CAS 免密流程Duo 集成类型错误或 Admin API 调用失败 → 日志中产生 API 错误与警告CAS 忽略账户状态并继续走认证流程对应配置项account-status-enabled的注释说明。回退意味着 CAS 会继续使用其他已配置的免密账户存储例如基于 LDAP/JSON/JPA 的自定义存储或常规认证方式进行认证因此该能力可以安全地叠加在现有免密方案之上作为第一优先级的快速通道。七、适用前提与限制基于源码与配置定义使用本功能前需要明确以下前提与限制依赖 Duo Admin API免密账户查询依赖 Admin 集成凭据且需要 Duo 集成类型具备查询用户账户详情的权限无权限或错误类型会触发 API 告警并回退流程用户数据要求用户必须已注册有效邮箱和移动设备否则无法收到推送通知与 Duo 自身免密能力无关本功能不启用 Duo 的 Passwordless/PassKey也不与 CAS 的 WebAuthn 能力冲突它们各自独立版本前提配置类标注since 6.4.0账户存储实现与验证动作标注since 7.1.0实际可用能力以当前仓库版本为准多提供商选择若配置了多个 Duo 提供商且多个均开启免密开关findFirst()将选择第一个查询到合格账户的提供商建议按业务优先级排列cas.authn.mfa.duo的索引顺序。八、参考资料仓库内可直接查阅功能文档Passwordless-Authentication-Storage-DuoSecurity.md配置属性定义DuoSecurityMultifactorAuthenticationProperties.java自动配置类DuoSecurityPasswordlessAuthenticationConfiguration.java、CasDuoSecurityAutoConfiguration.java账户存储实现DuoSecurityPasswordlessUserAccountStore.java免密验证动作DuoSecurityVerifyPasswordlessAuthenticationAction.javaAdmin API 实现DefaultDuoSecurityAdminApiService.java账户状态枚举DuoSecurityUserAccountStatus.java单元测试DuoSecurityPasswordlessUserAccountStoreTests.java赞分享后端认证鉴权单点登录【免费下载链接】casApereo CAS - Identity Single Sign On for all earthlings and beyond.项目地址https://gitcode.com/gh_mirrors/ca/cas点击查看免费下载相关推荐Apereo CAS 密码免密认证集成 Apache Syncope 作为密码免密账户存储Apereo CAS 密码免密认证集成 Apache Syncope 作为密码免密账户存储 本篇指南聚焦于如何在 Apereo CAS 中将 Apache后端认证鉴权单点登录Triton 开发者会议纪要解读Block Pointer 迁移路线、性能测试基础设施与 2025 开发者峰会规划Triton 开发者会议纪要解读Block Pointer 迁移路线、性能测试基础设施与 2025 开发者峰会规划 2025 年 5 月 1 日的 Trito后端认证鉴权单点登录为 Tolaria 的脚本化 HTML 块实现自定义协议tolaria-html-block 打包交付架构解析为 Tolaria 的脚本化 HTML 块实现自定义协议 tolaria html block 打包交付架构解析 Tolaria 是一款基于 Markdown后端认证鉴权单点登录上一篇为什么选择SprinklesAndroid ORM框架对比分析下一篇Cherry Studio自定义模型添加私有AI模型的完整流程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
