Apereo CAS Spring Cloud 配置服务器接入 Amazon DynamoDb 配置源实战指南

原创2026-09-25 12:12:201,448 阅读
文章标签:后端认证鉴权单点登录

Apereo CAS Spring Cloud 配置服务器接入 Amazon DynamoDb 配置源实战指南

CAS 除了可以从本地配置文件、Git、JDBC、MongoDb 等来源加载配置外,还支持将 Amazon DynamoDb 作为 Spring Cloud 配置服务器的属性来源,把键值对形式的配置集中存放在 DynamoDb 表中,并在运行时动态增删改查。本文基于当前仓库中的 Configuration-Server-Management-SpringCloud-DynamoDb.md 展开,结合 cas-server-support-configuration-cloud-dynamodb 模块的源码、配置模型与测试用例,带你完成依赖引入、参数配置、表结构理解与动态更新验证,掌握一套可实际部署的 DynamoDb 配置中心方案。

功能定位:DynamoDb 作为 CAS 的配置属性来源

CAS 使用 DynamoDb 来“定位(locate)属性和设置”,其工作方式与 Spring Cloud Config 的 Bootstrap 阶段一致:在 Spring 环境初始化早期,通过 PropertySourceLocator 从 DynamoDb 中读取所有键值对,注入到 Spring Environment 中,供 CAS 后续的属性绑定逻辑使用。

这一能力由 cas-server-support-configuration-cloud-dynamodb 模块提供。需要特别说明的是,该配置模块并非仅限 Spring Cloud 配置服务器使用——原文档明确提示:

The configuration modules provided here may also be used verbatim inside a CAS server overlay and do not exclusively belong to a Spring Cloud Configuration server.

也就是说,你可以把它直接放进 CAS 服务器 overlay,让 CAS 自身直接从 DynamoDb 拉取设置,也可以把它放进独立的 Spring Cloud Config Server 中充当配置源,两种场景完全通用。

第一步:在 WAR overlay 中引入依赖

在 CAS WAR overlay 的 build.gradle 中添加如下依赖(对应 Maven 坐标为 org.apereo.cas:cas-server-support-configuration-cloud-dynamodb):

implementation "org.apereo.cas:cas-server-support-configuration-cloud-dynamodb:${project.'cas.version'}"

该模块源码位于仓库的 support/cas-server-support-configuration-cloud-dynamodb 目录,其核心启动装配类是 CasDynamoDbCloudConfigBootstrapAutoConfiguration.java。从源码可以看到,该自动配置受特性开关约束:

@ConditionalOnFeatureEnabled(feature = CasFeatureModule.FeatureCatalog.CasConfiguration, module = "dynamodb")
@AutoConfiguration
public class CasDynamoDbCloudConfigBootstrapAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "dynamoDbPropertySourceLocator")
    public PropertySourceLocator dynamoDbPropertySourceLocator() {
        return new DynamoDbPropertySourceLocator();
    }
}

它只负责在上下文中注册一个 PropertySourceLocator Bean。若已存在同名 Bean,则不会重复注册。对应的配置属性类在 SpringCloudConfigurationProperties.java 中声明,并通过 @RequiresModule(name = "cas-server-support-configuration-cloud-dynamodb") 与模块建立关联。

自动创建的表结构:DynamoDbCasProperties

CAS 启动时如果允许建表,会自动创建名为 DynamoDbCasProperties 的表,表中每行就是一个配置项,结构非常简单,只有两个字段:

{
    "name": "the-setting-name",
    "value": "the-setting-value"
}
  • name:配置项名称,即 Spring 环境中的属性键,例如 cas.authn.accept.users;
  • value:配置项值,以字符串形式存储,例如 casuser::WHATEVER。

这两个列名由枚举 DynamoDbColumnNames.java 统一定义(NAME("name")、VALUE("value")),表名常量则定义在 DynamoDbPropertySource.java 中:public static final String TABLE_NAME = "DynamoDbCasProperties";。

从建表请求的源码 DynamoDbPropertySourceLocator.java 可以看到底层建表细节:

  • name 列被定义为 哈希键(HASH Key,分区键),类型为字符串(ScalarAttributeType.S);
  • 默认采用 预置吞吐量(Provisioned Throughput),读写容量单位均为 10(PROVISIONED_THROUGHPUT = 10);
  • 建表前会先检查表是否已存在,创建后等待表进入 ACTIVE 状态再继续。

配置参数:cas.spring.cloud.dynamo-db 前缀全解

所有 DynamoDb 配置源的参数统一挂载在 cas.spring.cloud.dynamo-db 前缀之下(源码中的常量前缀为 cas.spring.cloud.dynamoDb,见 DynamoDbPropertySourceLocator.java;Spring Boot 宽松绑定规则允许 dynamo-db 与 dynamoDb 两种写法等价)。

参数分为两部分:继承自 AbstractDynamoDbProperties.java 的 DynamoDb 专属参数,以及继承自 BaseAmazonWebServicesProperties.java 的 AWS 通用参数。

DynamoDb 专属参数

参数 类型 默认值 说明
cas.spring.cloud.dynamo-db.drop-tables-on-startup boolean false 启动时是否先删除已存在的表再重建,通常用于测试环境。对应 DynamoDbPropertySourceLocator 中 createSettingsTable(client, deleteTables) 的 deleteTables 参数
cas.spring.cloud.dynamo-db.prevent-table-creation-on-startup boolean false 是否阻止 CAS 在启动时自动建表。置为 true 时跳过建表逻辑,直接读取现有表
cas.spring.cloud.dynamo-db.time-offset int 0 时间偏移量
cas.spring.cloud.dynamo-db.read-capacity long 10 预置模式下的读容量单位
cas.spring.cloud.dynamo-db.write-capacity long 10 预置模式下的写容量单位
cas.spring.cloud.dynamo-db.billing-mode enum PROVISIONED 计费模式:PROVISIONED(预置吞吐,适合流量可预测、成本可控的场景)或 PAY_PER_REQUEST(按请求付费,适合流量不可预测的新表)
cas.spring.cloud.dynamo-db.local-instance boolean false 是否连接本地 DynamoDb 实例(如 DynamoDB Local),此时无需真实凭证,主要用于开发与测试
cas.spring.cloud.dynamo-db.dax.* object — Amazon DynamoDB Accelerator(DAX)内存缓存相关嵌套参数

AWS 通用参数(连接与凭证)

参数 类型 默认值 说明
cas.spring.cloud.dynamo-db.credential-access-key String — AWS 访问密钥(必填)
cas.spring.cloud.dynamo-db.credential-secret-key String — AWS 密钥(必填)
cas.spring.cloud.dynamo-db.region String AWS_GLOBAL AWS 区域,如 us-east-1
cas.spring.cloud.dynamo-db.endpoint String — 自定义端点,连接 DynamoDB Local 时必须设置为 http://localhost:8000
cas.spring.cloud.dynamo-db.profile-name / profile-path String — AWS 凭证配置文件(profile)名称与路径
cas.spring.cloud.dynamo-db.max-connections int 10 最大连接数
cas.spring.cloud.dynamo-db.connection-timeout Duration 5000ms 连接超时
cas.spring.cloud.dynamo-db.socket-timeout Duration 5000ms Socket 超时
cas.spring.cloud.dynamo-db.client-execution-timeout Duration 10000ms 客户端执行超时
cas.spring.cloud.dynamo-db.use-reaper boolean false 是否启用 reaper
cas.spring.cloud.dynamo-db.proxy-host / proxy-password / proxy-username String — 代理主机与代理认证信息
cas.spring.cloud.dynamo-db.retry-mode String STANDARD AWS SDK 重试模式,可选 STANDARD、LEGACY
cas.spring.cloud.dynamo-db.local-address String — 本地绑定地址

说明:上述 credential-access-key、credential-secret-key、region、endpoint 在配置模型中被标记为 @RequiredProperty(必填)。但 AWS SDK 的凭证链(ChainingAWSCredentialsProvider)也支持从环境变量、EC2 元数据等默认位置获取凭证,因此即使不显式配置访问密钥,只要运行环境具备 AWS 凭证也可工作。

这些参数通过 AmazonEnvironmentAwareClientBuilder.java 从 Spring Environment 中按 前缀.参数名 读取并构建 DynamoDbClient。其 build 方法会依次处理凭证提供者、区域(未配置时回退到 AWS_GLOBAL)和端点覆盖(endpointOverride),见 AmazonEnvironmentAwareClientBuilder.java。

源码级工作原理:从定位器到属性源

该模块的运行时核心链路由三个类组成,位于 org.apereo.cas 包下:

  1. DynamoDbPropertySourceLocator.java:实现 Spring Cloud 的 PropertySourceLocator 接口,在 Bootstrap 阶段被调用。其 locate(Environment) 方法流程为:

    • 用 AmazonEnvironmentAwareClientBuilder 以 cas.spring.cloud.dynamoDb 为前缀构建 DynamoDbClient;
    • 读取 prevent-table-creation-on-startup 设置,若为 false 则调用 createSettingsTable 自动建表;
    • 返回一个 DynamoDbPropertySource 实例。
  2. DynamoDbPropertySource.java:继承 Spring 的 EnumerablePropertySource<DynamoDbClient> 并实现 CAS 的 MutablePropertySource<DynamoDbClient> 接口,是真正的读写载体。它在构造时调用 refresh() 扫描全表,将 name 列收集到内存中的 propertyNames 集合;随后:

    • getProperty(name):先检查 propertyNames 是否包含该键,包含时再通过 GetItemRequest 按 name 主键精确读取对应 value;
    • setProperty(name, value):通过 PutItemRequest 写入/覆盖一行(name 与 value 两列),并把 name 加入内存集合;
    • removeProperty(name):通过 DeleteItemRequest 按主键删除一行;
    • removeAll():先 Scan 全表,再逐行删除,清空整个配置表;
    • refresh():Scan 全表并过滤掉缺少 name 或 value 列的脏数据,重建 propertyNames 集合。
  3. CasDynamoDbCloudConfigBootstrapAutoConfiguration.java:负责在 Spring 上下文中装配上述 Locator,受 CasConfiguration 特性与 dynamodb 模块开关控制。

模块对应的集成测试位于 DynamoDbCloudConfigBootstrapConfigurationTests.java,它完整演示了“建表 → 写入 → 读取 → 动态更新 → 删除”的端到端流程,可作为理解该模块行为的权威参考。

本地开发与验证:结合 DynamoDB Local

由于 DynamoDb 是托管服务,本地验证需要先起一个 DynamoDB Local 实例。仓库的 CI 脚本 run-dynamodb-server.sh 给出了现成做法——用 Docker 在 8000 端口启动官方本地镜像:

export DOCKER_IMAGE="amazon/dynamodb-local:3.3.1"
docker run --quiet --rm -d -p 8000:8000 --name "dynamodb-server" ${DOCKER_IMAGE}

随后在 CAS 的配置文件中指向该本地实例,并关闭建表保护以便让 CAS 自动建表:

# 连接本地 DynamoDB Local
cas.spring.cloud.dynamo-db.endpoint=http://localhost:8000
cas.spring.cloud.dynamo-db.local-instance=true
cas.spring.cloud.dynamo-db.region=us-east-1
# 本地实例可随意填写测试凭证
cas.spring.cloud.dynamo-db.credential-access-key=test
cas.spring.cloud.dynamo-db.credential-secret-key=test
# 允许 CAS 启动时自动创建 DynamoDbCasProperties 表
cas.spring.cloud.dynamo-db.prevent-table-creation-on-startup=false

这正是测试类 DynamoDbCloudConfigBootstrapConfigurationTests.java 中使用的参数组合(该测试还额外通过 @EnabledIfListeningOnPort(port = 8000) 要求本地 8000 端口可用)。测试的初始化逻辑会先建表,再写入一条配置:

values.put(DynamoDbColumnNames.NAME.getColumnName(), AttributeValue.builder().s("cas.authn.accept.users").build());
values.put(DynamoDbColumnNames.VALUE.getColumnName(), AttributeValue.builder().s("casuser::WHATEVER").build());

随后断言 cas.authn.accept.users 被正确绑定到 CAS 的 CasConfigurationProperties 中。也就是说,你可以直接把 cas.authn.accept.users=casuser::WHATEVER 这类原本写在 application.properties 里的配置,改成 DynamoDb 表中的一行记录来加载。

运行时动态配置更新与 casConfig 端点

原文档特别强调:该能力支持运行时动态配置更新(dynamic configuration updates at runtime)。这得益于 DynamoDbPropertySource 实现了 CAS 的 MutablePropertySource 接口——它不仅是只读的配置源,还支持 setProperty、removeProperty、removeAll 等写操作。

写操作既可以通过编程方式调用,也可以通过 CAS 提供的 Actuator 端点 casConfig 对外暴露。该端点由 cas-server-support-reports 模块提供(见 CasConfigurationEndpoint.java),定义如下:

操作 HTTP 方式与路径 作用
加密 POST /actuator/casConfig/encrypt 使用配置加密器加密属性值
解密 POST /actuator/casConfig/decrypt 解密属性值
检索 POST /actuator/casConfig/retrieve 按名称模式/值/属性源过滤并返回配置项
更新 POST /actuator/casConfig/update 更新属性,返回实际生效的属性源列表
删除 DELETE /actuator/casConfig 删除整个属性源中的配置

其对应的端点测试 CasConfigurationEndpointTests.java 展示了典型调用形态,例如 POST /actuator/casConfig/retrieve 检索属性、POST /actuator/casConfig/update 更新属性、DELETE /actuator/casConfig 清空属性源等,均需按 CAS 的 Actuator 安全策略开启访问(测试中以 management.endpoint.casConfig.access=UNRESTRICTED 为例)。

由于端点默认访问权限为 Access.NONE(见 CasConfigurationEndpoint.java),生产环境务必通过 management.endpoint.casConfig.access 或 Spring Security 规则限制为授权用户可访问,避免配置内容被未授权读写。

小结与适用场景

至此,一套完整的 DynamoDb 配置源方案已经清晰:

  1. 依赖:在 overlay 中引入 cas-server-support-configuration-cloud-dynamodb(可同时用于 Spring Cloud Config Server 与 CAS 服务器本体);
  2. 表:CAS 自动创建 DynamoDbCasProperties 表,每行即一个 name/value 配置对,name 为哈希键;
  3. 参数:所有设置挂在 cas.spring.cloud.dynamo-db 前缀下,覆盖凭证、区域、端点、吞吐量、计费模式与本地实例等;
  4. 能力:配置源同时支持读写,配合 casConfig Actuator 端点可在运行时动态增删改查配置;
  5. 本地化:用 amazon/dynamodb-local 镜像加 5 行配置即可在开发环境完整复现。

当你的 CAS 集群希望摆脱 Git 文件式配置、让运维通过 AWS 生态统一管理配置项时,DynamoDb 配置源是一个可靠且与 AWS 基础设施天然集成的选择。

登录后查看全文
cas