Apereo CAS Spring Cloud 配置服务器接入 Amazon DynamoDb 配置源实战指南
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 包下:
-
DynamoDbPropertySourceLocator.java:实现 Spring Cloud 的
PropertySourceLocator接口,在 Bootstrap 阶段被调用。其locate(Environment)方法流程为:- 用
AmazonEnvironmentAwareClientBuilder以cas.spring.cloud.dynamoDb为前缀构建DynamoDbClient; - 读取
prevent-table-creation-on-startup设置,若为false则调用createSettingsTable自动建表; - 返回一个
DynamoDbPropertySource实例。
- 用
-
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集合。
-
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 配置源方案已经清晰:
- 依赖:在 overlay 中引入
cas-server-support-configuration-cloud-dynamodb(可同时用于 Spring Cloud Config Server 与 CAS 服务器本体); - 表:CAS 自动创建
DynamoDbCasProperties表,每行即一个name/value配置对,name为哈希键; - 参数:所有设置挂在
cas.spring.cloud.dynamo-db前缀下,覆盖凭证、区域、端点、吞吐量、计费模式与本地实例等; - 能力:配置源同时支持读写,配合
casConfigActuator 端点可在运行时动态增删改查配置; - 本地化:用
amazon/dynamodb-local镜像加 5 行配置即可在开发环境完整复现。
当你的 CAS 集群希望摆脱 Git 文件式配置、让运维通过 AWS 生态统一管理配置项时,DynamoDb 配置源是一个可靠且与 AWS 基础设施天然集成的选择。