为 Apereo CAS 打造自定义 Surrogate 认证账户存储:从接口到运行时的完整扩展指南
发布时间:2026/9/25 5:09:15来源:尧图网络
后端认证鉴权单点登录【免费下载链接】casApereo CAS - Identity Single Sign On for all earthlings and beyond.项目地址https://gitcode.com/gh_mirrors/ca/cas点击查看免费下载本文面向需要为 Apereo CAS 设计专属代理认证Surrogate Authentication账户存储的开发者基于官方文档 Surrogate-Authentication-Storage-Custom.md 及其对应的源码实现完整讲解自定义SurrogateAuthenticationService的接口契约、内置实现模式、Bean 注册与覆盖机制帮助你用AutoConfiguration方式把自有账户体系数据库、目录服务、内部 API 等无缝接入 CAS 运行时。一、理解 Surrogate 认证的核心契约SurrogateAuthenticationService 接口自定义账户存储的第一步是吃透 CAS 定义的核心抽象接口SurrogateAuthenticationService。该接口位于 surrogate-api 模块是 CAS 判断某人能否以他人身份认证的统一入口。接口本身是一个FunctionalInterface只声明了一个抽象方法其余均为带默认实现的方法因此实现成本很低package org.apereo.cas.authentication.surrogate; FunctionalInterface public interface SurrogateAuthenticationService { // 通配账户标记授权账号可用 * 表示可冒充任何用户 String WILDCARD_ACCOUNT *; // 默认 Bean 名称覆盖内置实现时必须以同名注册 String BEAN_NAME surrogateAuthenticationService; // 认证负载中记录代理用户名的属性名 String AUTHENTICATION_ATTR_SURROGATE_USER surrogateUser; // 认证负载中记录原始主账号的属性名 String AUTHENTICATION_ATTR_SURROGATE_PRINCIPAL surrogatePrincipal; // 标记 surrogate 认证已启用 String AUTHENTICATION_ATTR_SURROGATE_ENABLED surrogateEnabled; default boolean canImpersonate(String surrogate, Principal principal, Optional? extends Service service) throws Throwable { return false; } CollectionString getImpersonationAccounts(String username, Optional? extends Service service) throws Throwable; default boolean isWildcardedAccount(String surrogate, Principal principal, Optional? extends Service service) throws Throwable { val accounts getImpersonationAccounts(principal.getId(), service); return isWildcardedAccount(accounts, service); } default boolean isWildcardedAccount(CollectionString accounts, Optional? extends Service service) { return accounts.size() 1 accounts.contains(SurrogateAuthenticationService.WILDCARD_ACCOUNT); } default void collectSurrogateAttributes(AuthenticationBuilder builder, String surrogateUser, String principal) { builder.addAttribute(AUTHENTICATION_ATTR_SURROGATE_USER, surrogateUser); builder.addAttribute(AUTHENTICATION_ATTR_SURROGATE_PRINCIPAL, principal); builder.addAttribute(AUTHENTICATION_ATTR_SURROGATE_ENABLED, Boolean.TRUE); } }各方法的职责如下方法职责必须实现canImpersonate(surrogate, principal, service)判断主账号principal是否有权以surrogate身份认证否默认false但自定义实现通常需要覆写getImpersonationAccounts(username, service)返回某主账号可冒充的全部用户名集合是唯一抽象方法isWildcardedAccount(...)判断某账号是否为通配账户返回集合恰好只含*即视为通配否collectSurrogateAttributes(...)把代理信息写入认证构建器供下游审计与消费否可直接复用默认实现从接口可以看出自定义存储的核心工作量集中在getImpersonationAccounts返回可冒充名单和canImpersonate判定单次冒充是否被允许两个方法上。代理认证完成后CAS 会通过collectSurrogateAttributes在认证结果中沉淀surrogateUser、surrogatePrincipal、surrogateEnabled三个标准属性这既是下游业务取用代理身份的通道也是审计日志的关键数据来源。二、先看内置实现五种开箱即用的账户存储模式在动手自定义之前先梳理 CAS 自带的SurrogateAuthenticationService实现族它们分布在 surrogate-core 模块SimpleSurrogateAuthenticationService基于cas.authn.surrogate.simple.surrogates配置的静态映射表主账号到可冒充名单的硬编码映射MapString, List。它是其余实现的行为基准canImpersonateInternal直接查表判断。JsonResourceSurrogateAuthenticationService从 JSON 文件加载同样的映射结构并通过FileWatcherService监听文件变更、热加载账户数据见JsonResourceSurrogateAuthenticationService.java中loadServices的getEligibleAccounts().clear()后putAll重建逻辑。GroovySurrogateAuthenticationService委托 Groovy 脚本执行canAuthenticate、getAccounts、isWildcardAuthorized三个方法脚本位置由cas.authn.surrogate.groovy.location指定。ChainingSurrogateAuthenticationService组合器。把多个SurrogateAuthenticationService串成链canImpersonate采用任一命中即通过getImpersonationAccounts则合并所有实现的名单。REST / JDBC / LDAP 实现分别位于cas-server-support-surrogate-authentication模块的 REST、JDBC、LDAP 子配置中对应cas.authn.surrogate.rest、jdbc、ldap配置段。这些实现全部继承自抽象基类BaseSurrogateAuthenticationService位于 BaseSurrogateAuthenticationService.java该基类为我们自定义实现提供了现成的授权骨架——这也是自定义存储时最值得借鉴的模板。基类的公共授权逻辑免费继承的能力BaseSurrogateAuthenticationService.canImpersonate是一个final方法定义了完整的判定链Override public final boolean canImpersonate(final String surrogate, final Principal principal, final Optional? extends Service service) throws Throwable { val serviceAuthorized isServiceAuthorizedForImpersonation(principal, service); return serviceAuthorized (surrogate.equalsIgnoreCase(principal.getId()) || isPrincipalAuthorizedForImpersonation(surrogate, principal, service) || isWildcardedAccount(surrogate, principal, service) || canImpersonateInternal(surrogate, principal, service)); }判定链依次是服务级授权若请求带有 Service则通过RegisteredServicePrincipalAccessStrategyEnforcer校验访问策略并要求该注册服务为WebBasedRegisteredService且其surrogatePolicy.isEnabled()为真源码见isServiceAuthorizedForImpersonation。没有指定 Service 时直接放行。本人即代理surrogate与principal.getId()忽略大小写相等视为通过。属性级授权当cas.authn.surrogate.core.principal-attribute-names与principal-attribute-values配置了正则表达式时用主账号的对应属性值做正则匹配见isPrincipalAuthorizedForImpersonation中的RegexUtils.findFirst。通配账户账户名单恰好为*。自定义判定调用抽象方法canImpersonateInternal(surrogate, principal, service)——这就是留给自定义存储的唯一扩展点。因此最省力的自定义方式是继承BaseSurrogateAuthenticationService只实现canImpersonateInternal与getImpersonationAccounts即可免费获得服务级授权、属性级授权与通配账户的全部内建能力。测试用例 SimpleSurrogateAuthenticationServiceTests.java 中可以看到这套逻辑的完整验证包括通过membership属性正则(ad|st|su).*放行、注册服务要求impersonationyes属性时抛出PrincipalException、以及surrogatePolicy关闭时拒绝冒充。三、编写自定义账户存储官方推荐的完整步骤原文档给出的骨架如下这是自定义账户存储的最小完整形态package org.apereo.cas.custom; AutoConfiguration EnableConfigurationProperties(CasConfigurationProperties.class) public class MySurrogateConfiguration { Bean public SurrogateAuthenticationService surrogateAuthenticationService() { ... } }围绕这个骨架把它落成一份可运行、可覆盖内置实现的完整实现需要补齐四个关键细节。3.1 步骤一实现 SurrogateAuthenticationService或继承基类推荐直接继承BaseSurrogateAuthenticationService只写两个方法。以对接自建 REST API 的存储为例package org.apereo.cas.custom; import org.apereo.cas.authentication.principal.Principal; import org.apereo.cas.authentication.principal.Service; import org.apereo.cas.authentication.surrogate.BaseSurrogateAuthenticationService; import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.services.RegisteredServicePrincipalAccessStrategyEnforcer; import org.apereo.cas.services.ServicesManager; import org.springframework.context.ConfigurableApplicationContext; public class MyRestBackedSurrogateAuthenticationService extends BaseSurrogateAuthenticationService { public MyRestBackedSurrogateAuthenticationService( final ServicesManager servicesManager, final CasConfigurationProperties casProperties, final RegisteredServicePrincipalAccessStrategyEnforcer principalAccessStrategyEnforcer, final ConfigurableApplicationContext applicationContext) { super(servicesManager, casProperties, principalAccessStrategyEnforcer, applicationContext); } Override protected boolean canImpersonateInternal(final String surrogate, final Principal principal, final Optional? extends Service service) throws Throwable { // 调用自有账户系统判断 principal.getId() 是否有权冒充 surrogate return getImpersonationAccounts(principal.getId(), service).contains(surrogate); } Override public CollectionString getImpersonationAccounts(final String username, final Optional? extends Service service) throws Throwable { // 从数据库 / LDAP / 内部 API 查询 username 可冒充的账号列表 return List.of(casuser, banderson); } }3.2 步骤二在 AutoConfiguration 中声明同名 Bean关键点在于Bean 方法名即 Bean 名称必须与接口常量一致SurrogateAuthenticationService.BEAN_NAME也就是surrogateAuthenticationService。原因见下文覆盖机制——CAS 内置定义带有ConditionalOnMissingBean(name surrogateAuthenticationService)只有同名 Bean 才能触发条件跳过实现以你为准的替换效果。package org.apereo.cas.custom; import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService; import org.apereo.cas.configuration.CasConfigurationProperties; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ScopedProxyMode; AutoConfiguration EnableConfigurationProperties(CasConfigurationProperties.class) public class MySurrogateConfiguration { Bean RefreshScope(proxyMode ScopedProxyMode.DEFAULT) public SurrogateAuthenticationService surrogateAuthenticationService( final ServicesManager servicesManager, final CasConfigurationProperties casProperties, final RegisteredServicePrincipalAccessStrategyEnforcer principalAccessStrategyEnforcer, final ConfigurableApplicationContext applicationContext) { return new MyRestBackedSurrogateAuthenticationService( servicesManager, casProperties, principalAccessStrategyEnforcer, applicationContext); } }EnableConfigurationProperties(CasConfigurationProperties.class)让整个 CAS 平台的类型安全配置体系前缀cas.*对本配置类可见后续可以通过casProperties.getAuthn().getSurrogate()读取cas.authn.surrogate.*下所有子配置例如在自定义实现里读取你自己的扩展配置段。3.3 步骤三把配置类注册进 CAS 运行时AutoConfiguration组件并不会被组件扫描自动发现必须按照 Spring Boot 约定显式注册。原文档末尾指向的 Configuration-Management-Extensions.md 给出了标准做法在你的模块或 Overlay 工程中创建文件src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports在其中写入配置类的全限定名每行一个org.apereo.cas.custom.MySurrogateConfigurationCAS 自身的所有模块也正是通过这套机制发布配置组件的——例如cas-server-support-surrogate-authentication模块的 SurrogateAuthenticationConfiguration.java 即被声明为Configuration功能等价于AutoConfiguration并被ConditionalOnFeatureEnabled(feature CasFeatureModule.FeatureCatalog.SurrogateAuthentication)守护只有启用 surrogate 特性时才装配。提示如果你把自定义配置类放进 CAS Overlay 工程中编译可能需要为类路径补充相应 CAS 模块依赖如cas-server-support-surrogate-api、cas-server-support-surrogate-core、cas-server-core-api-configuration-model等具体以CasConfigurationProperties、ServicesManager等类型所在的模块为准。3.4 步骤四理解条件覆盖确保你的实现真正生效CAS 内置的surrogateAuthenticationServiceBean 定义带有条件注解RefreshScope(proxyMode ScopedProxyMode.DEFAULT) ConditionalOnMissingBean(name SurrogateAuthenticationService.BEAN_NAME) Bean public SurrogateAuthenticationService surrogateAuthenticationService(...) { ... }见 SurrogateAuthenticationConfiguration.java 中SurrogateAuthenticationServiceConfiguration内部类。这意味着只要应用上下文中已经存在名为surrogateAuthenticationService的 BeanCAS 内置定义就会被跳过你的实现自动胜出。这也是为什么上一步要求 Bean 方法名严格取surrogateAuthenticationService。同理内置的simple/json/groovy三个BeanSupplierSurrogateAuthenticationService也各自带ConditionalOnMissingBean(name simpleSurrogateAuthenticationService)等条件你可以按需单独替换其中某一个。值得补充的是内置装配的底层逻辑SurrogateAuthenticationServiceConfiguration会把所有BeanSupplierSurrogateAuthenticationServicesimple、json、groovy 各自按配置条件装配其余为 null收集起来经排序后统一交给ChainingSurrogateAuthenticationService组合使用。也就是说多个账户存储可以同时并存、链式叠加而一旦你定义了自定义的同名 Bean就会整体接管这条链。若希望自定义实现与内置实现叠加而非替换可考虑以其他 Bean 名称声明并参考ChainingSurrogateAuthenticationService的组合模式自行编排。四、让自定义 Bean 支持动态刷新RefreshScope 的价值CAS 是 Spring Cloud 应用配置中心属性变更可以触发上下文刷新。将自定义 Bean 标注RefreshScope(proxyMode ScopedProxyMode.DEFAULT)后Bean 实例会在配置刷新时重新创建从而热加载你的账户存储例如重建与数据库的连接、重读缓存名单。CAS 内置的所有 surrogate 相关 Bean 都采用了这一模式建议自定义实现保持同样的风格。如果你的账户数据本身频繁变动如 JSON 文件被运维实时修改还可以仿照JsonResourceSurrogateAuthenticationService的做法在实现内部挂载FileWatcherService对数据源做文件监听热加载而不必依赖配置刷新。五、验证你的实现从测试到运行时观察5.1 参照官方测试用例编写单元验证内置实现的测试集中在 surrogate-authentication 模块的测试目录包括SimpleSurrogateAuthenticationServiceTests.java验证静态映射表、属性正则授权cas.authn.surrogate.core.principal-attribute-names/principal-attribute-values、注册服务访问策略拦截PrincipalException、surrogatePolicy关闭时拒绝授权GroovySurrogateAuthenticationServiceTests.java 与 JsonResourceSurrogateAuthenticationServiceTests.java验证脚本与 JSON 文件两种数据源ChainingSurrogateAuthenticationServiceTests.java验证多存储组合的合并与任一命中逻辑。自定义实现可参照上述用例以casuser作为主账号、构造含membership等属性的Principal逐一断言canImpersonate与getImpersonationAccounts的返回。5.2 运行时观察点代理认证成功后CAS 会在认证结果中写入三个标准属性surrogateUser、surrogatePrincipal、surrogateEnabled可在审计日志与后续的 Principal 解析、服务授权环节观察到。若你的自定义实现生效体现在行为上就是getImpersonationAccounts返回的名单会成为代理认证流程中可选账号的候选集canImpersonate的判定结果决定代理是否放行。六、写在最后自定义存储与官方存储方案的关系CAS 官方文档体系为 surrogate 认证提供了完整的存储方案矩阵自定义实现是这条路径的最后一环适用于官方方案无法覆盖的专有账户体系Surrogate-Authentication-Storage-Simple.md配置硬编码映射Surrogate-Authentication-Storage-JSON.mdJSON 文件Surrogate-Authentication-Storage-Groovy.mdGroovy 脚本Surrogate-Authentication-Storage-JDBC.md、Surrogate-Authentication-Storage-LDAP.md、Surrogate-Authentication-Storage-REST.md自定义实现与上述方案共享同一套接口契约、同一套服务级授权surrogatePolicy与属性级授权cas.authn.surrogate.core.*逻辑唯一的区别只是账户名单从哪里来。吃透SurrogateAuthenticationService接口、复用BaseSurrogateAuthenticationService的判定链、以同名 Bean 完成条件覆盖即可用最少代码把自有账户体系接入 CAS 代理认证与官方存储方案平起平坐、随时切换。赞分享后端认证鉴权单点登录【免费下载链接】casApereo CAS - Identity Single Sign On for all earthlings and beyond.项目地址https://gitcode.com/gh_mirrors/ca/cas点击查看免费下载相关推荐开源AI工程实战中小企业如何低成本部署智能应用开源AI工程实战中小企业如何低成本部署智能应用 面对AI技术浪潮许多中小企业和创业团队面临这样的困境看到大公司AI应用风生水起自己却不知从何入手。技术后端认证鉴权单点登录战略级文档迁移架构语雀Lake到Markdown的无损转换方案战略级文档迁移架构语雀Lake到Markdown的无损转换方案 在知识管理数字化转型的关键阶段企业面临着从封闭格式到开放标准的战略迁移挑战。语雀Lake格式后端认证鉴权单点登录Triton 开发者会议纪要解读Block Pointer 迁移路线、性能测试基础设施与 2025 开发者峰会规划Triton 开发者会议纪要解读Block Pointer 迁移路线、性能测试基础设施与 2025 开发者峰会规划 2025 年 5 月 1 日的 Trito后端认证鉴权单点登录上一篇如何快速构建高效Windows桌面应用WinForms完整指南下一篇推荐一款创新的命令行活动监控工具 —— vtop创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网