主题
配置体系全解析
唯一配置源是 classpath 上的
base.properties,但-D/环境变量只能兜底不能覆盖;真正能改线上行为的只有sys_conf表(30 秒生效)和 WAR 的WEB-INF/classes同名遮蔽。
1. 配置分层总览
| 层 | 载体 | 加载方 | 生效方式 | 可否改后立即生效 |
|---|---|---|---|---|
| L1 基础设施 | base.properties | DecryptingPropertyPlaceholderConfigurer(captcha-base/src/main/resources/base-application-context.xml:16-19) | 启动期解析占位符 | 否,必须重启 |
| L2 模块自治 | elasticsearch.properties / image.properties / produce.properties / frequency.properties / irisk.properties | 各自 Factory、Manager | 启动期读取 | 否 |
| L3 后台权限 | captcha-admin/src/main/resources/spring-security.xml | application-context.xml:18 import | 启动期装配 Security 链 | 否 |
| L4 容器环境变量 | Dockerfile ENV(XMS/XMX/META/JVM_EXTRA_CONFIG/APPNAME/CONTEXT_PATH…) | start.sh、entrypoint.sh | 进程启动参数 | 否 |
| L5 动态配置 | sys_conf 表 | SysConfLoader(captcha-base/.../sys/conf/SysConfLoader.java:38) | 30 秒轮询 | 是 |
判断标准很简单:想“改完就生效”的开关放 sys_conf;连接串、端口、路径一律在 L1/L2,改了必须重启。
2. base.properties(L1,最关键)
位于 captcha-base/src/main/resources/base.properties,被四个 WAR/JAR 全部继承(captcha-base 是公共依赖)。按用途分八组:
2.1 数据库与 Redis
| key | 默认值 | 含义 | 必填 |
|---|---|---|---|
db.driver | com.mysql.cj.jdbc.Driver | 驱动;切国产库改 org.opengauss.Driver 等 | 是 |
db.url | jdbc:mysql://10.104.68.141:23306/captcha_custom_liantong | 单库地址,注释里给出 panweidb 备用写法(:12-13) | 是 |
db.connectionProperties | characterEncoding=utf8;autoReconnect=true;connectTimeout=60000;socketTimeout=130000;useSSL=false;serverTimezone=Asia/Shanghai | 连接参数,时区必须显式给,否则统计跨天错位 | 是 |
db.username / db.password | admin / 明文或 Enc(...) | 账号密码 | 是 |
redis.redisType | 1 | 1 单机 / 2 sentinel / 3 sharding / 4 cluster / 5 ncr(:3-4) | 是 |
redis.nodes | 10.44.45.14:6379 | 节点列表 | 是 |
redis.pwd / redis.masterName | 空 | 密码 / sentinel 主节点名 | 视类型 |
redis.timeout / redis.maxAttempt | 1000 / 10 | 超时毫秒、重试次数 | 否 |
2.2 服务地址(启动强依赖)
| key | 含义 | 缺失后果 |
|---|---|---|
getconf.api.server | get/check API 域名,不带 http:// | 启动直接失败:GetConfController.java:85 的 Validate.notEmpty 抛异常 |
getconf.static.servers | JS 静态资源域名,逗号分隔 | 同上(:86),且 SDK 加载 404 |
static.server | 后台更新静态版本时的内网地址,带 http://(:17-18) | 前端版本发布失败 |
image.upload.server / image.download.server | 图片上传/下载地址 | 取图 404 |
2.3 对象存储、邮件与监控
| key | 默认值 | 说明 |
|---|---|---|
s3.accessKey / s3.secretKey / s3.bucketName / s3.endPoint / s3.region | 示例值 | S3UploadService 是唯一生效的上传实现,这五项缺一图片写不进去 |
smtp.host/port/protocol/username/password/from/auth/ssl.enable | smtp.corp.netease.com:465、smtps、true | 收件人列表不在 properties,在 sys_conf.smtp.conf(init-data.sql:4303) |
prometheus.base.url | http://10.104.68.13:30090 | 后台拉指标判警的 Prometheus 地址 |
captcha.pod.prefix | 空 | Pod 名前缀,如 yd-;用于把指标维度对齐到服务名 |
grafana.dashboard.url / timeout / enabled | 有示例 | 后台仪表盘跳转 |
2.4 安全与合规
| key | 默认值 | 说明 |
|---|---|---|
security.restrict.domains | 空 | 全局 referer 白名单,空=不限制(:35-36);单应用维度用 nc_app.domain + restrict_domain |
license.publicKey | 空 | 只用于后台导入 License 时的解密。为空不阻塞启动,但取图 100% 失败 |
sso.tokenCheck.url / sso.appKey / sso.appSecret / sso.passWordSuffix | 示例值 | 后台 SSO 单点登录;不用 SSO 可保留但不启用 |
log4a.sync | 内网地址 | 4A 日志审计推送 |
is.backup / zoneId | false / CN31 | 主备标记与机房标识,影响监控面板标签 |
2.5 卓望 IP 数据服务
zhuowang.ip.enabled=false 是总闸(:72),关闭时不产生任何外呼。开启后需要 4 个完整 URL(token / scene / risk-score / address)、app-key/app-secret、timeout-ms(默认 50,很激进),以及 token-expire-unit(第三方文档单位为分钟,配错会让 token 提前或延后过期)。超时只有 50ms,链路里应视为“可失败依赖”。
2.6 磁盘与节点匹配(监控专用)
disk.mountpoint 形如 captcha-admin:/home,captcha-server:/home(:86)——“模块名:挂载点”,一个模块只能一个挂载点;node.host.redis.match / node.host.panwei.match / node.host.es.match 是宿主机名匹配串,用于把 Pod 指标对齐到对应中间件所在物理机。这三项填错的结果是监控面板“中间件无数据”,不影响业务。
3. 模块级属性文件(L2)
| 文件 | 位置 | 加载方 | 关键项 |
|---|---|---|---|
elasticsearch.properties | captcha-base/src/main/resources/ | ElasticsearchClientFactory(captcha-base/.../service/ElasticsearchClientFactory.java:63) | cluster.nodes、connect.timeout=5000、read.timeout=10000、username/password、ssl.enabled=false、connection.pool.size=20、index.retention.days=180 |
irisk.properties | 同上 | captcha-server/.../risk/IRiskModel.java:228 loadBasicConfig | irisk.enable=0(默认关闭风险检测)、irisk.pn、5 个 businessId.*(web/android/ios/hm/applet)、secret.id/key、domain、pub.domain、fail.token |
frequency.properties | captcha-server/src/main/resources/ | ConfigManager(captcha-server/.../rules/frequency/ConfigManager.java:78,getResourceAsStream) | redis.type=4、redis.nodes(集群 3 节点)、redis.server.pwd、frequency.refresh.interval=1 |
image.properties | captcha-image/src/main/resources/ | captcha-image 上传/读取路径 | 仅一项 upload_file_path=/java-web-dir/custom-image/ |
produce.properties | captcha-schedule/src/main/resources/ | 预生成 worker | 仅一项 click.produce.thread.count=20 |
frequency.properties是第二套 Redis,与base.properties的redis.*完全独立:前者只存高频计数(高频规则命中判定),后者存验证码 token、会话、名单缓存。两套指向同一个 cluster 但 key 不冲突是历史约定,混用会导致计数与业务缓存互相淘汰。ConfigManager用getResourceAsStream直读 classpath,没有任何外部覆盖通道,唯一改法是遮蔽 jar 内同名文件。
irisk.enable=0 意味着私有化默认不调用易盾云端风控:私有化客户没有 irisk 授权,开了也只会超时。.codemaker 知识文档中也把 IRisk 标为“可选增强”。
4. spring-security.xml(L3)
captcha-admin/src/main/resources/spring-security.xml 是后台唯一的鉴权声明,263 行里 190 行是 intercept-url。规则要点:
- 全部用
hasAuthority('XXX')表达式,权限字符串就是数据库sys_permission.permission的值,新增接口必须同时加 XML 行 + 插权限行,否则 403。 - 匹配顺序即声明顺序,具体 URL 必须排前面(
:210-212注释明确写了这点)。 - 业务接口
/api/**整体放开(:196),仅/api/prometheus/warning/saveInfo.json单独收紧(:182)。 csrf disabled="true"(:221),跨站防护靠 referer 校验与同源部署。- 自定义
permissionRefreshFilter挂在FILTER_SECURITY_INTERCEPTOR之前(:224),每次请求实时从库刷新权限——改权限不用重新登录。 /health/**虽permitAll(:187),但代码里没有对应 handler,探针用它会 404。
5. sys_conf(L5,唯一热生效层)
结构见 build/db_init/mysql/mysql.sql:911,48 条初始数据见 init-data.sql:4255-4307。value_type 决定解析方式:1 数字、2 字符串(逗号分隔按字符串处理)、3 JSON。
运维常用的几条:
| key | 值示例 | 作用 | 改错的典型症状 |
|---|---|---|---|
frequency.enabled | true | 高频策略总闸 | 置 false 后黑名单与限流全部不触发 |
switchover.enabled | true | 自动切换验证码类型 | 关闭后智能无感知失败不再降级到点选 |
validate.referer.enabled | true | referer 校验总闸 | 置 false 等于对所有站点开放,高危 |
admin.session.timeout | 30 | 后台会话分钟数 | 改小后运维频繁掉登录 |
login.failed.max.attempts.count | 100 | 登录失败上限 | 与 account_operate_info 配合锁定账号 |
captcha.produce.count | JSON | 各类型预生成量 | 调大后 schedule 线程打满、对象存储暴涨 |
smtp.conf | {"to":"..."} | 告警收件人 | 空 → 告警邮件发不出 |
admin.special.char.check.conf | JSON 数组 | 后台特殊字符拦截白名单 | 配错导致正常保存被拦 |
6. 覆盖机制详解(与直觉相反的一节)
6.1 -D 与环境变量只能兜底
base-application-context.xml:16-19 用的是 PropertyPlaceholderConfigurer 子类,没有改 systemPropertiesMode,默认值是 SYSTEM_PROPERTIES_MODE_FALLBACK:解析 ${db.url} 时先查 properties 文件,文件里有值就绝不看 JVM 系统属性。所以:
bash
# 无效:base.properties 里 db.url 已有值,-D 被忽略
java -Ddb.url=jdbc:mysql://new-host:3306/db -jar app.jar这条约束直接决定了后面两种“正规”覆盖姿势。
6.2 WAR 模块:ConfigMap + subPath 遮蔽
WEB-INF/classes 在类加载顺序上优先于 WEB-INF/lib 里的 jar,因此把同名文件挂进解包后的 WEB-INF/classes 即可遮蔽 jar 内默认值(Dockerfile 已 unzip 到 /java-web-dir/webroot,captcha-server/Dockerfile:26):
yaml
# 只对 server/admin/image 三个 WAR 有效
volumeMounts:
- name: cfg
mountPath: /java-web-dir/webroot/WEB-INF/classes/base.properties
subPath: base.properties
- name: cfg
mountPath: /java-web-dir/webroot/WEB-INF/classes/frequency.properties
subPath: frequency.properties
volumes:
- name: cfg
configMap:
name: captcha-configcaptcha-admin 若走子路径部署,还要注入 CONTEXT_PATH(captcha-admin/Dockerfile:18),start.sh 会按它生成 Tomcat context 描述符。
6.3 JAR 模块:/java-app-dir/approot/conf 天然最优先
captcha-schedule 的 entrypoint.sh:61-62 把 ${APP_HOME}/conf 放在 classpath 第一位:
bash
CONF_DIR="${APP_HOME}/conf" # /java-app-dir/approot/conf
CLASSPATH="${CONF_DIR}:${APP_HOME}:${RUNTIME_LIB_DIR}/*"所以 schedule 不需要遮蔽技巧,直接把 *.properties 放进 /java-app-dir/approot/conf/ 就能覆盖,优先级高于加密包内所有 jar。
6.4 密文:Enc(...) 与 config.decrypt.key
DecryptingPropertyPlaceholderConfigurer(captcha-base/.../filter/DecryptingPropertyPlaceholderConfigurer.java:19-24)在解析每个属性值时判断是否为 Enc(...),是则交给 PropertiesEncryptUtil:
- 算法是 SM2(
SM2Util.decryptBase64,PropertiesEncryptUtil.java:36),不是 RSA; - 私钥取
System.getProperty("config.decrypt.key"),取不到时回退到代码里硬编码的 DEFAULT_PRIVATE_KEY(:32-35); - 因此生产环境必须显式传私钥,走容器环境变量注入 JVM 参数:
bash
JVM_EXTRA_CONFIG="-Dconfig.decrypt.key=MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYIt..."start.sh:9,26 把 JVM_EXTRA_CONFIG 拼进 CATALINA_OPTS;entrypoint.sh:69 用 JAVA_OPTS 承载。注意硬编码默认私钥的存在本身就是风险:任何拿到仓库的人都可解密默认密钥加密的值,交付时应轮换。
7. 启动强依赖项
| 依赖 | 校验点 | 缺失表现 |
|---|---|---|
getconf.api.server / getconf.static.servers | GetConfController.java:85-86 | 容器起不来,日志 apiServer conf is empty |
| MySQL 可达 + 表已建 | 连接池初始化 | 启动失败或首次请求超时 |
| Redis 可达 | JedisConnectionFactory | 启动失败 |
sys_conf 有数据 | SysConfLoader @PostConstruct | 能启动,但策略/预生成量走代码默认值 |
nc_licence + nc_license 有有效行 | LicenceCheckInterceptor + LicenseService | /api/** 被拦、取图失败 |
| S3 凭证 | S3UploadService | 取图 500 |
/java-web-dir/custom-image/ 可写 | image.properties | 图片服务启动后上游上传失败 |
| ES 可达 | ElasticsearchClientFactory | 行为日志写入失败,不阻塞取图/校验 |
8. 最小可运行配置清单
必须提供、且必须正确的 12 项:
db.url/db.username/db.passwordredis.redisType/redis.nodes/redis.pwdgetconf.api.server、getconf.static.servers(否则启动失败)image.download.server(客户端取图地址)s3.*五项(或改用本地图片服务并接受能力裁剪)frequency.properties的redis.nodes/redis.server.pwdelasticsearch.cluster.nodes+ 账号密码sys_conf48 条 +nc_licence/nc_license各 1 条license.publicKey(后台导入 License 用)prometheus.base.url(后台告警判警用)CONTEXT_PATH(仅 admin 子路径部署时)disk.mountpoint(仅需要磁盘告警时)
9. 脱敏 base.properties 骨架
properties
# ---- 环境标识 ----
system.isOnlineEnv=true
zoneId=CN31
is.backup=false
# ---- MySQL ----
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://mysql.example.internal:3306/captcha_custom
db.connectionProperties=characterEncoding=utf8;autoReconnect=true;connectTimeout=60000;socketTimeout=130000;useSSL=false;serverTimezone=Asia/Shanghai
db.username=${DB_USER}
db.password=Enc(<SM2 密文>)
# ---- Redis(业务缓存)----
redis.redisType=4
redis.nodes=redis-0.example.internal:6379,redis-1.example.internal:6379
redis.pwd=Enc(<SM2 密文>)
redis.timeout=1000
redis.maxAttempt=10
# ---- 服务地址(缺则启动失败)----
getconf.api.server=captcha-api.example.com
getconf.static.servers=captcha-static.example.com
image.upload.server=http://captcha-image.example.internal
image.download.server=https://cdn.example.com/captcha
static.server=http://captcha-image.example.internal
# ---- 对象存储 ----
s3.accessKey=Enc(<SM2 密文>)
s3.secretKey=Enc(<SM2 密文>)
s3.bucketName=captcha-prod
s3.endPoint=s3.example.com
s3.region=cn-north-1
# ---- 授权与安全 ----
license.publicKey=<License 签发方公钥>
security.restrict.domains=
zhuowang.ip.enabled=false
# ---- 监控 ----
prometheus.base.url=http://prometheus.example.internal:9090
captcha.pod.prefix=yd-
disk.mountpoint=captcha-admin:/home,captcha-demo:/home,captcha-image:/home,captcha-schedule:/home,captcha-server:/home10. 常见配置误区
| 误区 | 事实 |
|---|---|
用 -Ddb.url 覆盖库地址 | SYSTEM_PROPERTIES_MODE_FALLBACK 下无效,必须改文件或遮蔽 |
只配 base.properties 的 Redis | 高频计数走 frequency.properties,漏配 → 高频策略恒不命中或启动报错 |
改 sys_conf 后重启进程 | 不需要,30 秒自动生效;重启反而打断其他缓存 |
用 admin /health 做探针 | 只有 permitAll 声明无 handler,必 404;用 GET /login.html |
认为 license.publicKey 控制取图 | 它只管导入解密;取图看 nc_license 表行 |
认为 irisk.enable=1 就能启用云端风控 | 私有化无授权,开启只会拉长链路超时 |