- 后端
- 认证鉴权
- 单点登录
【免费下载链接】cas
Apereo CAS - Identity & Single Sign On for all earthlings and beyond.
导读
在 Apereo CAS 的多种认证策略(Authentication Policy)中,Not Prevented策略提供了一种“在不确定的安全事件面前宁可失败”的判定语义:仅当认证事件没有被PreventedException阻塞时才判定为满足。本文以官方文档 Configuring-Authentication-Policy-NotPrevented.md 为核心骨架,结合源码实现、配置属性模型与单元测试,深入讲解该策略的适用场景、配置方式、底层判定逻辑,以及它与“至少一个凭证通过”策略的继承关系,帮助你准确决定何时启用它。
一、策略语义:什么时候算“满足”
官方文档对该策略的定义只有一句话,但语义非常精确:
Satisfied if and only if the authentication event is not blocked by a
PreventedException.
即:当且仅当认证事件未被PreventedException阻塞时,该策略才被满足。这里的“阻塞”并不等于“认证失败”——它是认证基础设施层面的一种特殊错误状态。
PreventedException是什么
PreventedException定义在 api/cas-server-core-api-authentication/src/main/java/org/apereo/cas/authentication/PreventedException.java,源码注释明确说明:
Describes an error condition where authentication was prevented for some reason, e.g. communication error with back-end authentication store.
也就是说,它描述的是“认证因某种原因被阻止”的错误条件,典型的例子是与后端认证存储(如 LDAP、数据库)发生通信错误。这类错误不同于“用户名或密码错误”——后者是确定性的业务失败,而前者属于不确定性的系统级异常。它携带固定错误码:
public static final String CODE = "BLOCKED_AUTHN_REQUEST";当认证处理器在无法确定用户凭据是否有效(例如后端不可达)时抛出PreventedException,CAS 会将其记录到认证事件的失败列表(Authentication.getFailures())中。
一句话概括
Not Prevented策略 = “至少有一个凭证认证成功”且“没有出现PreventedException阻塞”。只要出现任何被阻止的认证尝试,无论其他凭据是否成功,策略整体判定为不满足。
二、源码实现:NotPreventedAuthenticationPolicy
策略的完整实现位于 core/cas-server-core-authentication-api/src/main/java/org/apereo/cas/authentication/policy/NotPreventedAuthenticationPolicy.java。
继承关系
public class NotPreventedAuthenticationPolicy extends AtLeastOneCredentialValidatedAuthenticationPolicy它直接继承自AtLeastOneCredentialValidatedAuthenticationPolicy(实现见 AtLeastOneCredentialValidatedAuthenticationPolicy.java),并在构造函数中强制传入tryAll = true:
public NotPreventedAuthenticationPolicy() { super(true); }这一点非常关键:父类中tryAll标志为true时,策略要求所有有资格处理本次认证事务的处理器(handler)都完成一次成功的认证,而不仅仅是“至少一个成功”。因此Not Prevented实际上是比AtLeastOneCredentialValidated更严格的一种“fail-closed(故障关闭)”变体——源码注释对此有明确说明:
This policy may be a desirable alternative to
AtLeastOneCredentialValidatedAuthenticationPolicyfor cases where deployers wish to fail closed for indeterminate security events.
核心判定逻辑
isSatisfiedBy方法分两步判定:
if (authentication == null) { LOGGER.warn("Authentication attempt is null and cannot satisfy policy"); return AuthenticationPolicyExecutionResult.failure(); } val fail = authentication.getFailures() .values() .stream() .anyMatch(failure -> failure.getClass().isAssignableFrom(PreventedException.class)); if (fail) { LOGGER.warn("Authentication policy has failed given at least one authentication failure is found to prevent authentication"); return AuthenticationPolicyExecutionResult.failure(); } return super.isSatisfiedBy(authentication, authenticationHandlers, applicationContext, context);逻辑顺序为:
- 空认证直接失败:认证事件为
null时记录 WARN 并返回失败; - 扫描失败列表:遍历
authentication.getFailures(),只要存在一个PreventedException(或其子类,通过isAssignableFrom判断),立即判定策略失败; - 委托父类:没有阻止性异常时,交给父类的
AtLeastOneCredentialValidated逻辑(此时tryAll=true),要求所有 handler 均有成功记录,且成功列表非空。
从源码结构可以推断,正是第 2 步的“一票否决”式检查,实现了文档所述的 “not blocked by aPreventedException” 语义:哪怕有一个凭据因系统错误被阻止,整个策略也不满足。
单元测试验证
策略行为在 NotPreventedAuthenticationPolicyTests.java 中有两个反向验证用例(标记@Tag("AuthenticationPolicy")):
verifyOperationPrevented:构建一个带PreventedException失败的认证事件,断言isSatisfiedBy返回失败;verifyOperationNotPrevented:构建一个无成功记录、也无被阻止异常的认证事件,断言策略仍然返回失败(因为tryAll=true下没有任何成功事务)。
两个用例共同印证:该策略既拒绝“被阻止”的认证,也拒绝“没有成功”的认证,是双重收紧的 fail-closed 语义。
三、全局启用:cas.authn.policy.not-prevented配置
策略的全局配置属性集中在cas.authn.policy.not-prevented命名空间下(官方文档通过{% include_cached casproperties.html properties="cas.authn.policy.not-prevented" %}自动渲染完整属性表)。
属性模型定义在 NotPreventedAuthenticationPolicyProperties.java,其本身没有额外字段,全部继承自 BaseAuthenticationPolicyProperties.java:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
cas.authn.policy.not-prevented.enabled | boolean | false | 是否启用该策略。只有设为true时该策略才会被装配 |
cas.authn.policy.not-prevented.name | String | 空 | 认证策略名称,便于日志与审计识别 |
cas.authn.policy.not-prevented.order | int | Ordered.LOWEST_PRECEDENCE | 策略在多策略组合中的执行顺序,数值越小优先级越高 |
典型配置示例(application.yml):
cas: authn: policy: not-prevented: enabled: true name: NotPreventedPolicy order: 10属性如何被装配为策略对象
cas.authn.policy.not-prevented在配置模型中挂载于 AuthenticationPolicyProperties.java 的notPrevented字段:
private NotPreventedAuthenticationPolicyProperties notPrevented = new NotPreventedAuthenticationPolicyProperties();装配逻辑位于 CoreAuthenticationUtils.java 的buildAuthenticationPolicyMap系列方法中:
if (policyProps.getNotPrevented().isEnabled()) { val policy = new NotPreventedAuthenticationPolicy(); return CollectionUtils.wrapList(configureAuthenticationPolicy(policy, policyProps.getNotPrevented())); }可以看到:只有enabled=true时才会实例化NotPreventedAuthenticationPolicy并加入策略集合;configureAuthenticationPolicy负责把name、order等元数据同步到策略对象。配置模型类标注了@RequiresModule(name = "cas-server-core-authentication", automated = true),表明该功能由核心认证模块提供,开箱即用,无需额外引入 support 模块。
CoreAuthenticationUtilsTests中也验证了该属性开关的行为(见 CoreAuthenticationUtilsTests.java):setEnabled(false)与setEnabled(true)分别对应策略的不装配与装配。
四、服务级粒度:Registered Service 认证策略条件
除了全局配置,Not Prevented还可以作为单个已注册服务(Registered Service)的认证策略条件使用,实现按应用定制的认证要求。这通过NotPreventedRegisteredServiceAuthenticationPolicyCriteria完成,实现见 NotPreventedRegisteredServiceAuthenticationPolicyCriteria.java:
public class NotPreventedRegisteredServiceAuthenticationPolicyCriteria implements RegisteredServiceAuthenticationPolicyCriteria { @Override public AuthenticationPolicy toAuthenticationPolicy(final RegisteredService registeredService) { return new NotPreventedAuthenticationPolicy(); } }从源码结构看,该 Criteria 直接把服务级条件转换为NotPreventedAuthenticationPolicy实例,因此服务级使用与全局启用共享同一套判定逻辑。相关服务解析逻辑在 RegisteredServiceAuthenticationPolicyResolverTests.java 中有测试覆盖。
在服务注册 JSON 中,可以通过authenticationPolicy.criteria类名方式引用该条件(具体序列化形态取决于服务注册文件的@class与 criteria 类型)。其可被 Jackson 反序列化,并由RegisteredServiceAuthenticationPolicyResolver在服务认证时解析为实际策略。
五、与相近策略的对比与选型建议
在 CAS 的认证策略体系中,Not Prevented与以下几个策略容易混淆,理解差异有助于正确选型:
| 策略 | 核心语义 | 失败关闭程度 |
|---|---|---|
AllAuthenticationHandlersSucceeded | 所有参与认证的 handler 都必须成功 | 高 |
NotPrevented | 不得出现PreventedException阻塞,且所有 handler 均成功(tryAll=true) | 高(含系统异常关闭) |
AtLeastOneCredentialValidated | 至少一个凭证通过验证即可(默认tryAll=false) | 低 |
从源码继承关系看,NotPrevented的关键价值在于把“系统级不确定错误(如后端认证存储通信失败)”纳入了策略判定:在多后端认证场景下,如果 LDAP 短暂不可达导致PreventedException,而密码本身可能完全正确,此时若使用宽松的“至少一个成功”策略,请求可能仍然放行;而NotPrevented会果断失败,避免在基础设施异常时做出错误的信任决策。
适用场景建议:
- 安全敏感、要求 fail-closed 的应用(如金融、政务系统)——后端异常宁可拒绝也不放行;
- 多认证后端并存、需要显式控制“部分后端故障”影响的部署;
- 服务级差异化:对个别高安全要求应用启用
NotPrevented,其他应用保持默认策略。
需要留意的代价:由于tryAll=true,该策略要求所有有资格处理的 handler 都成功,因此对后端可用性要求更高;任何单一后端故障都会导致整体认证失败,业务可用性会相应降低。启用前应评估后端高可用保障。
六、常见问题排查
- 配置了
not-prevented.enabled: true但策略未生效:检查属性路径是否为cas.authn.policy.not-prevented(注意是not-prevented连字符形式);同时确认该开关对应的装配分支在 CoreAuthenticationUtils.java 中按顺序生效,多个策略同时启用时会按order执行。 - 认证日志出现
Authentication policy has failed given at least one authentication failure is found to prevent authentication:表示存在PreventedException,应优先排查后端认证存储(LDAP、JDBC 等)的连通性与超时配置,而不是调整策略。 - 策略名称/顺序在审计中不清晰:通过
name与order属性为策略命名并排序,便于多策略组合时的日志定位。
小结
Not Prevented是 Apereo CAS 中面向“不确定安全事件”的 fail-closed 认证策略:它在“所有认证处理器均成功”的基础上,额外要求认证事件中不存在PreventedException阻塞。通过 NotPreventedAuthenticationPolicy.java 的失败扫描逻辑,以及cas.authn.policy.not-prevented配置与服务级 Criteria 两条装配路径,你可以灵活地在全局或单服务粒度启用这一策略,为高安全场景提供更严格、更可预期的认证判定。
- 后端
- 认证鉴权
- 单点登录
【免费下载链接】cas
Apereo CAS - Identity & Single Sign On for all earthlings and beyond.
相关推荐
Apereo CAS 认证策略之 All:多凭据全量认证的配置与实现原理
Apereo CAS 认证策略之 All:多凭据全量认证的配置与实现原理 导读 All 认证策略(Authentication Policy)是 Apereo
后端认证鉴权单点登录Apereo CAS 接入 Amazon Cloud Directory 认证:配置、原理与排障实战
Apereo CAS 接入 Amazon Cloud Directory 认证:配置、原理与排障实战 导读 本文基于 Apereo CAS 官方文档 AWS C
后端认证鉴权单点登录Apereo CAS 认证组件配置指南:认证管理器、认证处理器与认证策略体系
Apereo CAS 认证组件配置指南:认证管理器、认证处理器与认证策略体系 本文围绕 Apereo CAS 认证流程的骨架——认证管理器(Authentica
后端认证鉴权单点登录
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考