Apereo CAS Required 认证策略(Required Authentication Policy)详解与源码实现
2026/9/23 21:29:58 网站建设 项目流程
  • 后端
  • 认证鉴权
  • 单点登录

【免费下载链接】cas

Apereo CAS - Identity & Single Sign On for all earthlings and beyond.

项目地址:https://gitcode.com/gh_mirrors/ca/cas
点击查看免费下载

导读

本文围绕 Apereo CAS 认证策略家族中的Required(必选认证处理器)策略展开,讲解其核心语义:只有当指定的认证处理器(Authentication Handler)成功完成凭据校验时,整个认证事务才被视为满足策略要求。你将掌握该策略的全局配置方式(cas.authn.policy.req.*属性族)、tryAll标志的精确行为、底层判定逻辑的源码实现,以及如何在服务注册定义(Service Registry)中通过requiredAuthenticationHandlersAllowed策略标准(Criteria)对单个应用实施同等的强制约束。文中所有结论均可对照当前仓库源码验证,配置示例可直接复制使用。

一、什么是 Required 认证策略

在 CAS 的认证引擎中,一个认证事务(Authentication Transaction)可能同时携带多个凭据(Credential),并交由多个认证处理器(Authentication Handler)依次或并行校验。认证策略(Authentication Policy)负责在处理器执行完毕后,对认证结果进行整体判定,决定该次认证是否成立。

Required 认证策略的官方定义简洁而严格:

Satisfied if and only if a specified handler successfully authenticates its credential.

(仅当指定的处理器成功认证了它的凭据时,该策略才被满足。)

也就是说,该策略将认证是否成功收敛到某一个(或某几个)具名处理器之上:只要这些被点名的处理器中有一个产生了成功记录,策略即被满足;反之,即使其它处理器全部成功、唯独缺少指定的处理器,认证事务依然会被拒绝。这为"必须通过某种特定认证方式"的强约束场景提供了直接的配置化手段——例如强制用户必须通过 X.509 证书、LDAP、或某个自定义 Handler 完成认证,而不允许降级到其它方式。

从源码结构看,该策略归属于org.apereo.cas.authentication.policy包,其实现类为 RequiredAuthenticationHandlerAuthenticationPolicy,自 CAS 4.0.0 起引入,并一直沿用至今。

二、全局配置:cas.authn.policy.req

Required 认证策略属于 CAS 的全局认证策略,通过配置文件(application.properties/application.yml)中的cas.authn.policy.req.*属性族启用与定制,对应 CAS 7.x 的配置模型 RequiredAuthenticationHandlerAuthenticationPolicyProperties。

属性类型默认值说明
cas.authn.policy.req.enabledbooleanfalse是否启用该策略。该策略默认关闭,必须显式开启
cas.authn.policy.req.handlerNameStringhandlerName必须成功执行并验证凭据的认证处理器名称(必填项,见源码中@RequiredProperty注解);支持逗号分隔的多个名称,会按逗号拆分转换为集合
cas.authn.policy.req.tryAllbooleanfalse是否要求"尝试所有凭据"。开启后,将校验事务中收集到的凭据总数与"认证成功数 + 认证失败数"之和相等
cas.authn.policy.req.nameString策略名称(继承自 BaseAuthenticationPolicyProperties)
cas.authn.policy.req.orderintOrdered.LOWEST_PRECEDENCE(即Integer.MAX_VALUE策略在认证执行计划中的执行顺序,数值越小优先级越高

说明:handlerName在配置模型中被标记为@RequiredProperty,即该策略的必要属性。配置模型的默认值是占位符字符串handlerName,实际部署时必须显式替换为部署中真实存在的处理器名称,否则策略将检查一个不存在的处理器名,导致认证被拒绝。

一个最小可用的 YAML 配置示例如下:

cas: authn: policy: req: enabled: true handler-name: LdapAuthenticationHandler

等效的 properties 写法:

cas.authn.policy.req.enabled=true cas.authn.policy.req.handler-name=LdapAuthenticationHandler

若希望同时要求多个处理器,可通过逗号分隔指定:

cas: authn: policy: req: enabled: true handler-name: LdapAuthenticationHandler,X509CertificateAuthenticationHandler try-all: true

处理器名称从哪来

CAS 中每种认证方式都有默认的处理器名称(Handler Name)。例如默认的用户名密码认证处理器名为UsernamePasswordAuthenticationHandler、LDAP 处理器名为LdapAuthenticationHandler、X.509 处理器名为X509CertificateAuthenticationHandler等。绝大多数认证方式也允许通过自身配置属性为其指定自定义名称。需要获取部署中全部已注册处理器名称时,可以借助 CAS 的 Discovery Profile 能力,或查阅对应认证模块的配置属性文档。

三、配置装配:策略是如何被创建的

全局认证策略的装配集中在 CoreAuthenticationUtils.newAuthenticationPolicy(...) 方法中。从源码可见,当cas.authn.policy.req.enabled为真时,CAS 会:

  1. 使用 Spring 的StringUtils.commaDelimitedListToSethandlerName按逗号拆分为处理器名称集合;
  2. 以该集合与tryAll标志构造RequiredAuthenticationHandlerAuthenticationPolicy
  3. 通过configureAuthenticationPolicy(...)应用基类中定义的公共属性(名称、顺序等),并纳入认证执行计划。

源码片段(节选):

public static Collection<AuthenticationPolicy> newAuthenticationPolicy(final AuthenticationPolicyProperties policyProps) { if (policyProps.getReq().isEnabled()) { val requiredHandlerNames = org.springframework.util.StringUtils.commaDelimitedListToSet(policyProps.getReq().getHandlerName()); val policy = new RequiredAuthenticationHandlerAuthenticationPolicy(requiredHandlerNames, policyProps.getReq().isTryAll()); return CollectionUtils.wrapList(configureAuthenticationPolicy(policy, policyProps.getReq())); } // ... 其余策略(RequiredAttributes、AllHandlers、All 等)依次判定 }

可见req(Required)策略在全局策略集合中处于最先被检查的位置——只要它被启用,就会立即生效并返回策略集合。这与该策略"强约束"的定位一致:它优先于属性类、全处理器类等其它策略被考虑。

四、源码实现:判定逻辑与 tryAll 语义

4.1 核心判定:isSatisfiedByInternal

Required 策略的核心逻辑位于 RequiredAuthenticationHandlerAuthenticationPolicy.isSatisfiedByInternal(...):

@Override public AuthenticationPolicyExecutionResult isSatisfiedByInternal(final Authentication authn) { LOGGER.debug("Examining authentication successes for authentication handler [{}]", getHandlerNames()); if (!getHandlerNames().isEmpty()) { val credsOk = authn.getSuccesses() .keySet() .stream() .anyMatch(s -> getHandlerNames().contains(s)); if (!credsOk) { LOGGER.info("Required authentication handler(s) [{}] is not present in the list of successful authentications [{}]", getHandlerNames(), authn.getSuccesses().keySet()); return AuthenticationPolicyExecutionResult.failure(); } } LOGGER.trace("Authentication policy is satisfied"); return AuthenticationPolicyExecutionResult.success(); }

判定逻辑非常直观:

  • 取出本次认证结果Authentication对象中的成功记录集合getSuccesses()(键为处理器名称);
  • 对配置的处理器名称集合执行anyMatch:只要成功记录中存在任一被要求的处理器,策略即通过;
  • 若被要求的处理器在成功列表中一个都不存在,则输出INFO级日志(包含要求的处理器名与实际的成功处理器列表,便于排障)并返回失败。

注意该实现是"任一命中即满足"(anyMatch)语义:在handlerName指定多个处理器时,只要其中至少一个成功认证即通过,而非要求全部成功。

4.2 tryAll 的前置校验

tryAll标志的实际处理在基类 BaseAuthenticationHandlerAuthenticationPolicy.isSatisfiedBy(...) 中完成:

if (authn == null) { LOGGER.warn("Authentication attempt is null and cannot satisfy policy"); return AuthenticationPolicyExecutionResult.failure(); } var credsOk = true; val sum = authn.getSuccesses().size() + authn.getFailures().size(); if (this.tryAll) { credsOk = authn.getCredentials().size() == sum; } if (!credsOk) { LOGGER.warn("Number of provided credentials [{}] does not match the sum of authentication successes and failures [{}]. " + "Successful authentication handlers are [{}]", authn.getCredentials().size(), sum, authn.getSuccesses().keySet()); return AuthenticationPolicyExecutionResult.failure(); } return isSatisfiedByInternal(authn);

这里可以拆解出三层行为:

  1. 空认证保护:如果Authentication对象为null(认证事务未产生任何结果),策略直接判定失败;
  2. tryAll 校验:当tryAll=true时,要求"事务中收集的凭据数量 == 成功处理器数 + 失败处理器数"。这意味着事务中每一个凭据都必须被某个处理器尝试过——不允许存在未被处理(既未成功也未失败)的凭据。从源码结构看,该检查属于 Required 策略与其姊妹策略(如 Excluded 策略)共享的公共前置逻辑;
  3. 委托核心判定:前置校验通过后,调用子类的isSatisfiedByInternal(authn)执行真正的要求检查。

4.3 配置导出

基类还实现了toConfiguration()方法,将handlerNamestryAll导出为配置快照(BaseAuthenticationHandlerAuthenticationPolicy.toConfiguration()),供 CAS 的管理/审计端点呈现当前策略的实际状态,便于运维核对生效配置。

五、测试验证:策略的预期行为

仓库测试 DefaultAuthenticationManagerTests 直接验证了 Required 策略的注册与执行路径:

val policy = new RequiredAuthenticationHandlerAuthenticationPolicy( SimpleTestUsernamePasswordAuthenticationHandler.class.getSimpleName()); authenticationExecutionPlan.registerAuthenticationPolicy(policy); val manager = getAuthenticationManager(authenticationExecutionPlan); // ... 构造携带历史认证信息的认证事务 assertNotNull(manager.authenticate(testTransaction));

该用例(verifyTransactionWithAuthnHistoryAndAuthnPolicy)将"测试用用户名密码处理器"的类名作为必选处理器名称注册进认证执行计划,随后驱动DefaultAuthenticationManager完成一次认证事务并断言认证成功——证实了策略与认证管理器的集成链路是完整的:策略被注册进执行计划后,会在每次认证完成后被评估。

此外,同一测试类中还有使用new RequiredAuthenticationHandlerAuthenticationPolicy(Set.of(HANDLER_A), true)(启用tryAll)与new RequiredAuthenticationHandlerAuthenticationPolicy(HANDLER_B)的变体用例,分别覆盖了tryAll开启与单个处理器要求两种形态,可作为理解该策略行为边界的参考。

六、服务级约束:requiredAuthenticationHandlers 与 Allowed 标准

Required 语义不仅限于全局配置,还可以按服务(Registered Service)粒度施加。在服务注册定义(如 JSON 服务注册文件)中,通过authenticationPolicy.requiredAuthenticationHandlers字段指定该应用必须使用的认证处理器集合,完整示例见 Configuring-Service-AuthN-Policy:

{ "@class" : "org.apereo.cas.services.CasRegisteredService", "serviceId" : "https://app.example.org/.+", "name" : "ExampleApp", "id" : 1, "authenticationPolicy" : { "@class" : "org.apereo.cas.services.DefaultRegisteredServiceAuthenticationPolicy", "requiredAuthenticationHandlers" : ["java.util.TreeSet", [ "AuthNHandlerName" ]], "excludedAuthenticationHandlers" : ["java.util.TreeSet", [ ]] } }

其中requiredAuthenticationHandlers的语义为:"一组必须在 CAS 中可用并配置的认证处理器的标识/名称;当认证请求提交到 CAS 时,用于强制该服务定义只能使用承载该名称的认证策略。"

更进一步,服务定义还可以通过criteria指定AllowedAuthenticationHandlersRegisteredServiceAuthenticationPolicyCriteriaAllowed 标准),它在官方服务文档中被明确标注为"映射到全局的Required认证策略"。其 JSON 形式为:

{ "@class": "org.apereo.cas.services.CasRegisteredService", "serviceId": "^(https|imaps)://.*", "name": "Example", "id": 1, "authenticationPolicy": { "@class": "org.apereo.cas.services.DefaultRegisteredServiceAuthenticationPolicy", "requiredAuthenticationHandlers" : ["java.util.TreeSet", [ "JSON" ]], "criteria": { "@class": "org.apereo.cas.services.AllowedAuthenticationHandlersRegisteredServiceAuthenticationPolicyCriteria", "tryAll": false } } }

从源码看,AllowedAuthenticationHandlersRegisteredServiceAuthenticationPolicyCriteria.toAuthenticationPolicy(...) 的实现正是直接复用全局策略的实现类:

@Override public AuthenticationPolicy toAuthenticationPolicy(final RegisteredService registeredService) { val handlers = registeredService.getAuthenticationPolicy().getRequiredAuthenticationHandlers(); return new RequiredAuthenticationHandlerAuthenticationPolicy(handlers, this.tryAll); }

即:服务级的Allowed标准最终就是构造一个RequiredAuthenticationHandlerAuthenticationPolicy,其tryAll标志同样可由服务定义控制——服务文档对tryAll的说明与全局语义一致:"确保当前认证事务中收集的凭据总数与所有认证成功与失败之和匹配"。

这一设计使 Required 约束可以在全局(所有服务)与服务级(单个应用)两个层面灵活组合:全局开启后对所有认证请求生效,服务级criteria则可在单服务层面覆盖全局策略。

七、排障与注意事项

  • 启用后认证失败:首先确认handlerName是否为部署中真实注册的处理器名称。可查看 CAS 日志中策略输出的INFO级提示——Required authentication handler(s) [...] is not present in the list of successful authentications [...],其中会同时列出要求与实际的处理器名称集合,直接据此修正配置。
  • 多处理器语义是 anyMatchhandlerName指定多个名称时,任一成功即满足,并非全部要求。若需"全部指定处理器都必须成功"这类更严格约束,应评估 CAS 的AllHandlers(所有处理器成功)策略,而非 Required。
  • tryAll 与多凭据事务:当认证事务一次携带多个凭据且开启tryAll时,若存在未被任何处理器尝试的凭据,策略会在核心判定之前即失败。对于单凭据的常规登录流程,该标志通常保持默认的false即可。
  • 服务级与全局的关系:服务定义中的认证策略可以覆盖或补充全局策略,实际生效规则以 CAS 服务认证策略文档为准;如需对特定应用(而非全部请求)强制某种认证方式,优先采用服务级requiredAuthenticationHandlersAllowed标准。
  • 配置属性与模块依赖:该策略位于cas-server-core-authentication模块(见配置模型上的@RequiresModule(name = "cas-server-core-authentication", automated = true)),属于 CAS 核心能力,无需额外引入第三方支持模块即可使用。

八、总结

Required 认证策略是 CAS 认证策略体系中语义最直接、约束力最强的一类:它将认证成败绑定到具名处理器上,通过cas.authn.policy.req.handlerName一行配置即可强制"必须通过指定方式认证",同时以tryAll提供对多凭据事务完整性的前置校验。其实现 RequiredAuthenticationHandlerAuthenticationPolicy 逻辑精简、易于审查,并且通过服务注册定义中的requiredAuthenticationHandlersAllowed策略标准复用同一实现,使得同一套强约束既能全局生效、也能按应用灵活下发——这也是 CAS 在"认证方式不可降级"类安全需求上的标准答案。

  • 后端
  • 认证鉴权
  • 单点登录

【免费下载链接】cas

Apereo CAS - Identity & Single Sign On for all earthlings and beyond.

项目地址:https://gitcode.com/gh_mirrors/ca/cas
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询