- 后端
- 认证鉴权
- 单点登录
- 身份认证
【免费下载链接】cas
Apereo CAS - Identity & Single Sign On for all earthlings and beyond.
在 Apereo CAS 中,服务的访问授权(Service Access Strategy)决定了一个已注册的应用服务能否参与 CAS 认证/SSO 流程。本篇指南聚焦于 CAS 预留的自定义扩展点——RegisteredServiceAccessStrategyEnforcer:通过实现该接口并注册为 Spring Bean,你可以完全接管或补充 CAS 的服务授权判定逻辑,把自定义的授权规则(如对接内部风控系统、调用业务侧决策 API 等)无缝嵌入 CAS 运行时。读完本文,你将掌握自定义执行器的接口契约、注册方式、调用链原理,以及如何与 CAS 自带的 Groovy 执行器协同工作。
一、核心概念:什么是 RegisteredServiceAccessStrategyEnforcer
在 CAS 中,服务访问授权由两类组件协作完成:
- 访问策略(Access Strategy):与具体服务定义(
RegisteredService)绑定,声明式地描述"该服务在什么条件下允许访问",例如必须携带哪些属性、启用/禁用、是否允许 SSO 等,典型实现如DefaultRegisteredServiceAccessStrategy。 - 访问策略执行器(Access Strategy Enforcer):在认证与票据签发等关键节点被调用的执行入口,负责在运行时把访问策略真正"执行"起来,并产出可审计的结果。
本指南的主角是后者。RegisteredServiceAccessStrategyEnforcer是 CAS 从7.0.0起提供的扩展接口,其定义位于 RegisteredServiceAccessStrategyEnforcer.java:
@FunctionalInterface public interface RegisteredServiceAccessStrategyEnforcer extends AuditableExecution { }接口本身非常精简:它是一个标注了@FunctionalInterface的空接口,继承自AuditableExecution。真正需要实现的只有一个方法——来自AuditableExecution的:
AuditableExecutionResult execute(AuditableContext context) throws Throwable;AuditableExecution接口定义于 AuditableExecution.java,其 javadoc 明确指出:它是"CAS 服务器及其各模块在诸多审计点上的审计轨迹执行抽象",实现者通常还会使用 Inspektr 审计库的@Audit注解捕获审计数据。
二、注册自定义执行器(原文档核心步骤)
原文档给出的注册方式非常直接:编写一个@AutoConfiguration配置类,声明一个返回RegisteredServiceAccessStrategyEnforcer的@Bean,CAS 会在运行时自动拾取并用于授权执行。完整示例:
package org.apereo.cas; @AutoConfiguration @EnableConfigurationProperties(CasConfigurationProperties.class) public class MyConfiguration { @Bean @RefreshScope(proxyMode = ScopedProxyMode.DEFAULT) public RegisteredServiceAccessStrategyEnforcer myEnforcer() { return new MyRegisteredServiceAccessStrategyEnforcer(); } }其中需要你自行实现MyRegisteredServiceAccessStrategyEnforcer,例如:
public class MyRegisteredServiceAccessStrategyEnforcer implements RegisteredServiceAccessStrategyEnforcer { @Override public AuditableExecutionResult execute(final AuditableContext context) throws Throwable { // 读取上下文中的服务、认证、票据等信息,执行自定义授权判定 final AuditableExecutionResult result = AuditableExecutionResult.builder() .registeredService(context.getRegisteredService().orElse(null)) .service(context.getService().orElse(null)) .build(); if (/* 你的授权规则不通过 */) { result.setException(UnauthorizedServiceException.denied("Custom rule denied access")); } return result; } }注意@Bean方法上的@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT):它使该 Bean 具备热刷新能力,当外部属性变化触发 CAS 上下文刷新时,执行器实例可被自动重建。
让 CAS 识别你的 AutoConfiguration
@AutoConfiguration类需要被 Spring Boot 的自动装配机制发现。参照 Configuration-Management-Extensions.md 的说明,在 CAS Overlay 的src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中追加一行:
org.apereo.cas.MyConfiguration用 @Conditional 覆盖 CAS 自带执行器
CAS 自身也在 CasCoreServicesConfiguration.java 中注册了执行器相关 Bean,且大多带有@ConditionalOnMissingBean(name = ...)守卫:
@ConditionalOnMissingBean(name = AuditableExecution.AUDITABLE_EXECUTION_REGISTERED_SERVICE_ACCESS) @Bean @RefreshScope(proxyMode = ScopedProxyMode.DEFAULT) public AuditableExecution registeredServiceAccessStrategyEnforcer(...) { return new RegisteredServiceAccessStrategyAuditableEnforcer(applicationContext, principalAccessStrategyEnforcer); }AuditableExecution.AUDITABLE_EXECUTION_REGISTERED_SERVICE_ACCESS的值为"registeredServiceAccessStrategyEnforcer"。这意味着:只要你自定义的 Bean 使用相同的 Bean 名称,即可整体替换 CAS 默认的执行器。若你只想"追加"一层自定义判定而不替换默认实现,则直接按上文声明一个新命名的 Bean 即可——下文会说明两者在调用链上的差异。
三、执行链原理:自定义执行器如何被调用
CAS 默认执行器是RegisteredServiceAccessStrategyAuditableEnforcer(见 RegisteredServiceAccessStrategyAuditableEnforcer.java)。它的execute方法按优先级依次尝试多种判定路径:
@Override @Audit(action = AuditableActions.SERVICE_ACCESS_ENFORCEMENT, actionResolverName = AuditActionResolvers.SERVICE_ACCESS_ENFORCEMENT_ACTION_RESOLVER, resourceResolverName = AuditResourceResolvers.SERVICE_ACCESS_ENFORCEMENT_RESOURCE_RESOLVER) public AuditableExecutionResult execute(final AuditableContext context) { return byExternalAccessStrategyEnforcers(context) .or(() -> byServiceTicketAndAuthnResultAndRegisteredService(context)) .or(() -> byServiceAndRegisteredServiceAndTicketGrantingTicket(context)) .or(() -> byServiceAndRegisteredServiceAndPrincipal(context)) .or(() -> byServiceAndRegisteredServiceAndAuthentication(context)) .or(() -> byServiceAndRegisteredService(context)) .or(() -> byRegisteredService(context)) .orElseGet(() -> { /* ...默认拒绝... */ }); }关键的一步是byExternalAccessStrategyEnforcers(context)——它从 Spring 上下文中取出所有RegisteredServiceAccessStrategyEnforcer类型的 Bean,按顺序执行并收集第一个"执行失败"的结果:
protected Optional<AuditableExecutionResult> byExternalAccessStrategyEnforcers(final AuditableContext context) { return accessStrategyEnforcers .orderedStream() .filter(BeanSupplier::isNotProxy) .map(Unchecked.function(enforcer -> enforcer.execute(context))) .filter(Objects::nonNull) .filter(AuditableExecutionResult::isExecutionFailure) .findFirst(); }由此可以得出两个重要结论:
- 追加型注册(推荐):你新增的执行器 Bean 会被
byExternalAccessStrategyEnforcers一并收集执行。只要你的执行器返回带异常的结果(isExecutionFailure() == true),授权即被拒绝;若返回无异常的结果,CAS 会继续走内置的默认判定路径。 - 替换型注册:若用同名 Bean 覆盖
registeredServiceAccessStrategyEnforcer,你的执行器将完全替代上述整套判定逻辑,此时你需要自行处理各类上下文分支。
而执行器的调用入口位于核心认证服务 DefaultCentralAuthenticationService.java:
protected void enforceRegisteredServiceAccess(final AuditableContext audit) throws Throwable { val accessResult = configurationContext.getRegisteredServiceAccessStrategyEnforcer().execute(audit); accessResult.throwExceptionIfNeeded(); }也就是说,在 CAS 认证 / 授权 / 验证的关键流程中,一旦上下文被构造为AuditableContext(携带service、registeredService、principal等),就会触发执行器;若结果携带异常,则通过throwExceptionIfNeeded()抛出(如UnauthorizedServiceException),最终阻断请求。
上下文与结果对象
AuditableContext(AuditableContext.java):执行器收到的输入,通过@SuperBuilder构建,可包含service、registeredService、principal、authentication、serviceTicket、authenticationResult、ticketGrantingTicket、httpRequest、httpResponse以及扩展属性properties。这些字段均通过Optional暴露,你的自定义执行器应做好空值判断。AuditableExecutionResult(AuditableExecutionResult.java):执行器返回的结果,通过setException(...)标记失败,isExecutionFailure()判断是否失败,throwExceptionIfNeeded()抛出异常。此外还可通过addProperty(...)向结果追加自定义属性,供审计消费。
四、源码佐证:CAS 自带的 Groovy 执行器实现
CAS 仓库中提供了一个开箱即用的RegisteredServiceAccessStrategyEnforcer实现——GroovyRegisteredServiceAccessStrategyEnforcer(GroovyRegisteredServiceAccessStrategyEnforcer.java)。它可以作为你编写自定义执行器的绝佳参照:
public class GroovyRegisteredServiceAccessStrategyEnforcer implements RegisteredServiceAccessStrategyEnforcer, DisposableBean { private final ExecutableCompiledScript script; @Override public @Nullable AuditableExecutionResult execute(final AuditableContext context) throws Throwable { val args = new Object[]{context, LOGGER}; return script.execute(args, AuditableExecutionResult.class, true); } @Override public void destroy() { script.close(); } }它在 CasCoreServicesConfiguration.java 中通过条件 Bean 注册,配置项为cas.access-strategy.groovy.location:
@ConditionalOnMissingBean(name = "groovyRegisteredServiceAccessStrategyEnforcer") @Bean @RefreshScope(proxyMode = ScopedProxyMode.DEFAULT) @ConditionalOnMissingGraalVMNativeImage public RegisteredServiceAccessStrategyEnforcer groovyRegisteredServiceAccessStrategyEnforcer(...) { return BeanSupplier.of(RegisteredServiceAccessStrategyEnforcer.class) .when(BeanCondition.on("cas.access-strategy.groovy.location").exists() .given(applicationContext.getEnvironment())) .supply(() -> { val location = casProperties.getAccessStrategy().getGroovy().getLocation(); val scriptFactory = ExecutableCompiledScriptFactory.getExecutableCompiledScriptFactory(); return new GroovyRegisteredServiceAccessStrategyEnforcer(scriptFactory.fromResource(location)); }) .otherwiseProxy() .get(); }对应配置属性模型定义于 AccessStrategyProperties.java,即cas.access-strategy.groovy.location指向一个 Groovy 脚本资源。该脚本与自定义执行器走的是同一条调用链:脚本接收AuditableContext与LOGGER两个参数,返回AuditableExecutionResult。
仓库测试资源 ServiceAccessStrategy.groovy 给出了脚本的完整写法:
import org.apereo.cas.audit.* import org.apereo.cas.services.* def run(Object[] args) { def context = args[0] as AuditableContext def logger = args[1] logger.info("Checking access for ${context.registeredService}") if (context.service.isPresent()) { def service = context.service.get() logger.info("Service request is ${service.id}") if (service.id.startsWith("https://")) { def result = AuditableExecutionResult.builder().build() result.setException(UnauthorizedServiceException.denied("Service Unauthorized")) logger.error("Service ${service.id} is unauthorized by Groovy script") return result } } return AuditableExecutionResult.builder().build() }对应的测试类 GroovyRegisteredServiceAccessStrategyEnforcerTests.java 验证了"执行器返回失败结果"的完整链路:测试以cas.access-strategy.groovy.location=classpath:ServiceAccessStrategy.groovy启动上下文,注入registeredServiceAccessStrategyEnforcer与groovyRegisteredServiceAccessStrategyEnforcer,构造包含registeredService、service、authentication的AuditableContext后执行,并断言results.isExecutionFailure()为真。这同时印证了:自定义执行器只需在execute中返回带异常的结果,即可实现授权拒绝。
五、与 Groovy 全局策略的关系与选型建议
需要区分两个层面的 Groovy 扩展:
- 全局执行器层:即上文
GroovyRegisteredServiceAccessStrategyEnforcer(配置项cas.access-strategy.groovy.location),对所有服务的授权执行生效,属于"执行器"层面的策略。 - 服务级策略层:即
GroovyRegisteredServiceAccessStrategy(见 Service-Access-Strategy-Groovy.md),挂在单个服务定义 JSON 的accessStrategy字段上,属于"访问策略"层面的声明。
两者叠加使用时,判定顺序为:先执行全局执行器(含你的自定义执行器),再落入默认执行器的内置策略判定。若你的场景需要全站统一、强定制的授权逻辑(如调用外部决策引擎),自定义RegisteredServiceAccessStrategyEnforcer是正确选择;若只需为个别服务声明规则,优先使用服务级策略或 Groovy 脚本策略。
六、完整落地步骤小结
- 实现接口:在 CAS Overlay 工程中创建
MyRegisteredServiceAccessStrategyEnforcer implements RegisteredServiceAccessStrategyEnforcer,在execute(AuditableContext)中编写授权逻辑,返回AuditableExecutionResult(拒绝时setException(UnauthorizedServiceException.denied(...)))。 - 声明 Bean:编写
@AutoConfiguration+@EnableConfigurationProperties(CasConfigurationProperties.class)配置类,以@Bean+@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)暴露执行器;若需替换默认执行器,Bean 名称须为registeredServiceAccessStrategyEnforcer。 - 注册装配:将配置类全限定名写入
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。 - 验证:参照 GroovyRegisteredServiceAccessStrategyEnforcerTests.java 的写法编写集成测试,构造
AuditableContext并断言执行结果;通过 CAS 审计日志(SERVICE_ACCESS_ENFORCEMENT)确认执行器被调用。 - 上线观察:利用
@RefreshScope的能力,在属性变更触发上下文刷新时热更新执行器配置,避免频繁重启。
至此,你已经掌握了 CAS 自定义服务访问策略执行器的完整实现路径:从接口契约、Bean 注册、调用链原理到测试验证,均可在当前仓库中找到对应的源码与测试佐证,可据此把任意业务授权规则安全、可控地接入 CAS 的认证授权流程。
- 后端
- 认证鉴权
- 单点登录
- 身份认证
【免费下载链接】cas
Apereo CAS - Identity & Single Sign On for all earthlings and beyond.
相关推荐
Thorium 浏览器下载安装完整指南
Thorium 浏览器下载安装完整指南 如果你习惯了 Chrome,却嫌页面响应慢、新界面不合手,Thorium 值得装来试试。它是一款基于 Chromium
后端认证鉴权单点登录身份认证CAS 服务访问策略链(Service Access Strategy Chaining)配置与原理解析
CAS 服务访问策略链(Service Access Strategy Chaining)配置与原理解析 本篇指南围绕 Apereo CAS 注册服务(Regi
后端认证鉴权单点登录身份认证Apereo CAS 注册服务自定义属性(Service Custom Properties)配置指南
Apereo CAS 注册服务自定义属性(Service Custom Properties)配置指南 导读 本文介绍 Apereo CAS 中注册服务(Reg
后端认证鉴权单点登录身份认证
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考