- 后端
- 认证鉴权
- 单点登录
【免费下载链接】cas
Apereo CAS - Identity & Single Sign On for all earthlings and beyond.
本篇技术指南聚焦 Apereo CAS 中 SAML2 元数据的 Git 托管方案,覆盖 Service Provider(SP)元数据的解析与拉取、Identity Provider(IdP)元数据与密钥的存储推送、后台定时调度以及 Actuator 端点运维。读完本文,读者将能够独立完成cas-server-support-saml-idp-metadata-git模块的配置,并理解 CAS 底层如何通过 Git 仓库消除集群节点间元数据文件的手工同步。
背景与适用场景
当 CAS 以 SAML2 IdP 身份与多个 SP 对接时,每个 SP 的元数据文档(XML 格式)和签名证书(.pem格式)需要在所有 CAS 节点上保持一致。传统做法是将元数据文件手工复制到每个节点,这在集群规模扩大、SP 数量增多时极易出错。
Git 托管方案的核心思想是:将元数据文档作为 Git 仓库中的文件进行管理。CAS 启动或按需时从仓库拉取,需要更新时提交并推送,从而天然获得版本历史、变更审计和多节点一致性。
启用模块
在 CAS 部署(overlay)中引入以下模块:
<dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-saml-idp-metadata-git</artifactId> <version>${cas.version}</version> </dependency>从源码结构看,该模块的自动配置入口为 CasSamlIdPGitAutoConfiguration,它通过@Import同时引入 SP 元数据解析和 IdP 元数据管理两个子配置类,分别由独立的 Feature 开关控制:
SAMLServiceProviderMetadata+git→ SP 元数据解析SAMLIdentityProviderMetadata+git→ IdP 元数据管理
SP 元数据配置
注册服务定义
SAML 注册服务必须将metadataLocation字段设为git://,以此向 CAS 声明"该 SP 的元数据应从 Git 仓库获取"。示例如下:
{ "@class" : "org.apereo.cas.support.saml.services.SamlRegisteredService", "serviceId" : "the-entity-id-of-the-sp", "name" : "SAMLService", "id" : 1, "description" : "A Git-based metadata resolver", "metadataLocation" : "git://" }metadataLocation是路由关键字。从源码中 GitSamlRegisteredServiceMetadataResolver.supports() 可以看到,CAS 判断规则为:
val metadataLocation = service.getMetadataLocation(); return metadataLocation != null && (metadataLocation.trim().startsWith(getSourceId()) || (metadataLocation.trim().startsWith("http") && metadataLocation.trim().endsWith(".git")));即git://前缀,或以http开头且以.git结尾的 URL 均可触发 Git 解析器。getSourceId()返回固定值"git://"。
Git 仓库目录约定
根据源码 getMetadataDirectory(),SP 元数据文件固定存放在仓库根目录下的sp-metadata子目录中:
<repository-root>/ sp-metadata/ 1-SAMLService.xml ← 元数据文档 1-SAMLService.pem ← 签名证书(可选)文件命名规则为{serviceId}-{serviceName}.xml,签名证书为同名.pem文件。从parseFileIntoSamlMetadataDocument()方法(第 198–221 行)可以看到,CAS 解析文件时以第一个-分隔符提取serviceId(Long 类型)和name(字符串),若找不到分隔符则使用System.nanoTime()作为临时 id。
Git 连接配置
Git 连接参数继承自 BaseGitProperties,前缀为cas.authn.saml-idp.metadata.git。主要配置项如下:
| 属性 | 默认值 | 说明 |
|---|---|---|
cas.authn.saml-idp.metadata.git.repository-url | — | Git 仓库地址(必填,激活整个 Git 元数据解析) |
cas.authn.saml-idp.metadata.git.active-branch | master | 工作分支 |
cas.authn.saml-idp.metadata.git.username | — | 认证用户名 |
cas.authn.saml-idp.metadata.git.password | — | 认证密码或 Token |
cas.authn.saml-idp.metadata.git.ssh-session-password | — | SSH 会话密码 |
cas.authn.saml-idp.metadata.git.private-key-passphrase | — | SSH 私钥口令 |
cas.authn.saml-idp.metadata.git.strict-host-key-checking | true | 是否严格校验主机密钥 |
cas.authn.saml-idp.metadata.git.timeout | PT10S | 操作超时(ISO-8601 时长) |
cas.authn.saml-idp.metadata.git.push-changes | false | 是否允许推送变更 |
cas.authn.saml-idp.metadata.git.sign-commits | false | 是否对提交进行 GPG 签名 |
cas.authn.saml-idp.metadata.git.rebase | false | 拉取时使用 rebase 策略 |
cas.authn.saml-idp.metadata.git.clone-directory | 临时目录cas-saml-metadata | 本地克隆路径 |
其中clone-directory在 GitSamlMetadataProperties 构造函数 中默认设为系统临时目录下的cas-saml-metadata,生产环境建议显式指定一个持久化路径,避免临时目录清理导致重复克隆。
元数据解析调用链
当 CAS 需要解析某个 SP 的元数据时,调用链如下:
- 元数据解析计划(
SamlRegisteredServiceMetadataResolutionPlanConfigurer)遍历已注册的解析器; GitSamlRegisteredServiceMetadataResolver.supports()判断metadataLocation是否匹配;- 匹配后调用
load()方法:先执行gitRepository.pull()从远端拉取最新变更,然后在sp-metadata目录下列出所有.xml文件,逐一解析为SamlMetadataDocument对象; - 通过
CriteriaSet中的 entityID 条件过滤出目标 SP 的元数据,构建MetadataResolver返回。
拉取失败时仅记录 WARN 日志("Metadata files may be stale"),不会中断 SAML 认证流程,但 CAS 将使用本地最后一次成功拉取的元数据文件。
后台定时拉取(Schedule)
默认情况下,SP 元数据仅在需要解析时按需拉取。若希望 CAS 在后台周期性同步 Git 仓库,可启用调度器。配置前缀为cas.authn.saml-idp.metadata.git.schedule:
| 属性 | 默认值 | 说明 |
|---|---|---|
cas.authn.saml-idp.metadata.git.schedule.enabled | false | 是否启用后台调度 |
cas.authn.saml-idp.metadata.git.schedule.start-delay | PT60S | 首次执行前的延迟 |
cas.authn.saml-idp.metadata.git.schedule.repeat-interval | PT2H | 重复执行间隔 |
cas.authn.saml-idp.metadata.git.schedule.cron-expression | 空 | 自定义 Cron 表达式(设置后覆盖 interval) |
cas.authn.saml-idp.metadata.git.schedule.cron-time-zone | 空 | Cron 时区 |
从源码 GitSamlRegisteredServiceRepositoryScheduler 可以看到,调度器使用 Spring@Scheduled注解,同时支持cron和fixedDelay两种模式。执行逻辑非常简单:读取origin远端名称(缺省为"default"),然后调用gitRepository.pull()。
@Scheduled( cron = "${cas.authn.saml-idp.metadata.git.schedule.cron-expression:}", zone = "${cas.authn.saml-idp.metadata.git.schedule.cron-time-zone:}", initialDelayString = "${cas.authn.saml-idp.metadata.git.schedule.start-delay:PT60S}", fixedDelayString = "${cas.authn.saml-idp.metadata.git.schedule.repeat-interval:PT2H}")调度器 Bean 仅在schedule.enabled=true时创建,否则返回一个无操作代理,不消耗线程资源。
IdP 元数据管理
CAS 作为 SAML2 IdP 自身也需对外暴露元数据文档、签名密钥和加密密钥。Git 托管方案将这些工件同样纳入版本控制。
配置开关
IdP 元数据 Git 管理需要额外开启idp-metadata-enabled:
cas.authn.saml-idp.metadata.git.idp-metadata-enabled=true从 SamlIdPGitIdPMetadataConfiguration 可以看到,该开关与repository-url同时满足时才会激活 IdP 元数据的 Git 相关 Bean。
仓库中的文件布局
GitSamlIdPMetadataLocator 继承自FileSystemSamlIdPMetadataLocator,在仓库目录中按以下文件名查找工件:
| 文件 | 用途 |
|---|---|
idp-metadata.xml | IdP 元数据文档 |
idp-signing.key | 签名私钥 |
idp-signing.crt | 签名证书 |
idp-encryption.key | 加密私钥 |
idp-encryption.crt | 加密证书 |
fetchInternal()方法(第 37–67 行)每次被调用时先执行gitRepository.pull(),然后逐一读取上述文件,组装为SamlIdPMetadataDocument对象。
元数据加密与签名
IdP 元数据工件支持额外的加密/签名层,配置前缀为cas.authn.saml-idp.metadata.git.crypto,对应 EncryptionJwtSigningJwtCryptographyProperties。从配置类可以看到,加密和签名均使用 JWT 密码学属性模型,分别支持配置密钥大小:
- 加密密钥:
crypto.encryption.key-size(默认DEFAULT_STRINGABLE_ENCRYPTION_KEY_SIZE) - 签名密钥:
crypto.signing.key-size(默认DEFAULT_STRINGABLE_SIGNING_KEY_SIZE)
若crypto.enabled未显式开启,SamlIdPGitIdPMetadataConfiguration 第 62–63 行 会输出 INFO 日志提示"生产环境建议启用加密签名",并退化为CipherExecutor.noOp()。
生成器
GitSamlIdPMetadataGenerator负责将 CAS 生成的 IdP 元数据(含签名密钥对)写入 Git 仓库。它由SamlIdPMetadataGeneratorConfigurationContext提供上下文信息(实体 ID、各端点 URL 等),由 Git 仓库实例负责 commit 和 push。
Per-Service 元数据覆盖
全局 IdP 元数据可被特定服务覆盖。实现机制是:在元数据文档中设置appliesTo字段,CAS 以此字段构建仓库内的子目录路径。
从 GitSamlIdPMetadataLocator.getMetadataArtifactFile() 可以看到查找逻辑:
val directory = getMetadataDirectory(registeredService); // 基于 appliesTo 构建子目录 val file = new File(directory, fileName); if (file.exists() && file.canRead() && file.length() > 0) { return file; } return new File(defaultMetadataDirectory, fileName); // 回退到全局目录即优先从{appliesTo}子目录中查找工件,若该目录下文件不存在或为空,则回退到仓库根目录下的全局文件。这允许为特定 SP 使用独立的证书和元数据,而不影响其他 SP。
元数据的写入与删除
GitSamlRegisteredServiceMetadataResolver同时实现了SamlRegisteredServiceMetadataManager接口,提供完整的 CRUD 能力:
| 操作 | 方法 | 行为 |
|---|---|---|
| 保存 | store(document) | 写入{id}-{name}.xml和.pem→commitAll→push |
| 按名称删除 | removeByName(name) | 删除匹配文件 →commitAll→push |
| 按 ID 删除 | removeById(id) | 先findById再委托removeByName |
| 清空全部 | removeAll() | FileUtils.cleanDirectory→commitAll→push |
所有写操作均在本地 Git 仓库中完成 commit 后执行 push,提交信息格式为"Committed {name}"/"Removed {name}"/"Removed all metadata documents"。
Actuator 端点
CAS 通过 Actuator 端点samlIdPRegisteredServiceMetadata暴露已注册的 SAML SP 元数据信息,便于运维人员在不登录管理控制台的情况下检查当前各 SP 元数据的加载状态。
测试参考
模块自带的测试基类 BaseGitSamlMetadataTests 为GitSamlIdPMetadataGeneratorTests、GitSamlIdPMetadataLocatorTests和GitSamlRegisteredServiceMetadataResolverTests提供共享的 Git 仓库测试环境,可用于理解各组件在隔离条件下的行为边界。
配置清单速查
# --- Git 连接 --- cas.authn.saml-idp.metadata.git.repository-url=https://git.example.com/cas/saml-metadata.git cas.authn.saml-idp.metadata.git.active-branch=main cas.authn.saml-idp.metadata.git.username=cas-service cas.authn.saml-idp.metadata.git.password=${SAML_GIT_TOKEN} cas.authn.saml-idp.metadata.git.clone-directory=/var/cas/saml-metadata cas.authn.saml-idp.metadata.git.timeout=PT10S # --- IdP 元数据 --- cas.authn.saml-idp.metadata.git.idp-metadata-enabled=true cas.authn.saml-idp.metadata.git.crypto.enabled=true cas.authn.saml-idp.metadata.git.crypto.encryption.key-size=256 cas.authn.saml-idp.metadata.git.crypto.signing.key-size=256 # --- 后台调度 --- cas.authn.saml-idp.metadata.git.schedule.enabled=true cas.authn.saml-idp.metadata.git.schedule.start-delay=PT60S cas.authn.saml-idp.metadata.git.schedule.repeat-interval=PT2H以上配置项的完整定义参见 GitSamlMetadataProperties 及其父类 BaseGitProperties。
- 后端
- 认证鉴权
- 单点登录
【免费下载链接】cas
Apereo CAS - Identity & Single Sign On for all earthlings and beyond.
相关推荐
Apereo CAS 基于 Amazon S3 的 SAML2 动态元数据管理:SP 元数据解析与 IdP 元数据托管实战
Apereo CAS 基于 Amazon S3 的 SAML2 动态元数据管理:SP 元数据解析与 IdP 元数据托管实战 Apereo CAS 作为 SAML
后端认证鉴权单点登录CAS SAML2 元数据管理之 DynamoDb:将 IdP 与 SP 元数据持久化到 Amazon DynamoDb
CAS SAML2 元数据管理之 DynamoDb:将 IdP 与 SP 元数据持久化到 Amazon DynamoDb 本篇指南聚焦 Apereo CAS 中
后端认证鉴权单点登录Apereo CAS 中使用 Google Cloud Storage 托管 SAML2 IdP 元数据:JSON 文档结构、GCS 对象布局与每服务覆盖机制
Apereo CAS 中使用 Google Cloud Storage 托管 SAML2 IdP 元数据:JSON 文档结构、GCS 对象布局与每服务覆盖机制
后端认证鉴权单点登录
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考