配置项操作清单
本页是部署时该动哪些配置、动完怎么核对的操作清单,只覆盖三块:产物落位路径(app-market.deploy.*)、应用包安全(app-market.security.*)、平台形态与目录(app-market.edition 与 app-market.catalog.*),以及必须跟着一起确认的 security.* 与 xss.excludeUrls。
逐项索引(每个键在源码里的位置、注解兜底值、消费方)见 /reference/config。本页不重复那套表,只讲三件从索引表里看不出来的事:真正生效的值是哪一层给的、改完要不要重建 jar 或重启、哪几个键必须与别的组件对齐。
配置从哪来,谁覆盖谁
| 来源 | 位置 | 覆盖关系 |
|---|---|---|
| jar 内 yml | wujies-platform/wujies-admin/src/main/resources/application.yml 的 app-market: 段 | 唯一「写出来的值」;被下面两层覆盖 |
| 容器环境变量 | 1Panel 包内 docker-compose.yml 的 APP_MARKET_*(值来自安装表单) | 覆盖 yml |
JVM 系统属性 -D | 容器 deploy/docker/backend/entrypoint.sh;本机 script/prune/start-host.ps1 | 优先级最高 |
@Value 注解兜底 | Java 类里的 @Value("${app-market.xxx:默认值}") | 只在该键完全不存在时生效(不是一层配置) |
环境变量名与键名的换算规则是 Spring 的宽松绑定:点与横线都换成下划线、全大写。app-market.deploy.frontend-dir 对应 APP_MARKET_DEPLOY_FRONTEND_DIR。中间件地址(数据源 / Redis / RabbitMQ)不走 app-market.*,由 entrypoint.sh 导出成 SPRING_* 属性,见 /deploy/backend。
application.yml 被打进 wujies-admin.jar。改源码目录里的 yml 之后必须重建再重启:
# 在 wujies-platform 目录下;Maven 不在 PATH 时用 IDEA 自带的 mvn.cmd,见 /deploy/backend
mvn -pl wujies-admin -am -B install -DskipTests
powershell -NoProfile -ExecutionPolicy Bypass -File script\prune\start-host.ps11Panel 部署改的是「应用商店 → 已安装 → 参数」,保存后面板会重建容器,不需要手工 docker build;只有改了 Java 或前端 dist/ 才需要重建镜像。
app-market: 的完整配置树
下面是 application.yml 里真实存在的内容(207–289 行),public-key 的值已换成占位符:
app-market:
deploy:
# 后端插件 jar 落位目录,必须与宿主启动参数 -Dloader.path 一致
# ⚠️ loader.path 只加载该目录**根下**的 jar,不递归子目录
plugin-dir: plugins-dist
# 前端远程应用落位目录,必须与宿主 /remote-apps/ 的物理路径一致
# ⚠️ 前后端分离部署时必须改成共享/分发目录,否则前端服务取不到产物
frontend-dir: ../wujies-admin/app-market-dist/remote-apps
# 应用包安全检查:产物白名单 + 签名校验
security:
# off 完全不检查(等价于改造前的行为)
# warn 检查并记录问题,但照常安装/上架
# enforce 有问题就拒绝 ← 当前
mode: enforce
# 包签名公钥(X.509 SubjectPublicKeyInfo 的 PEM,或裸 base64)
# 留空表示尚未配置校验能力 —— 此时 enforce 会拒绝所有包并在报错里说明原因
public-key: |
-----BEGIN PUBLIC KEY-----
<你的公钥>
-----END PUBLIC KEY-----
# frontend/ 下允许的文件类型。前端目录是宿主的静态资源目录,
# 白名单之外一律拒绝;且 .jsp/.php/.war/.jar 等无论如何都不允许(代码里硬拒)
allowed-frontend-extensions: >-
.js,.mjs,.cjs,.css,.json,.svg,.png,.jpg,.jpeg,.gif,.webp,.ico,.bmp,
.woff,.woff2,.ttf,.otf,.eot,.map,.txt,.html,.wasm
# 版本形态:official(官方版,默认) / client(用户版)
edition: official
# 应用目录(catalog)的访问方式
catalog:
# local 直连本库(默认,单机部署用这个)
# remote 走 base-url 指向的目录服务(多部署集中时用)
mode: local
# 目录服务地址;mode=remote 时必填,否则启动即失败
base-url: ''
# 服务间共享令牌。服务端与调用方配成一致;
# 服务端留空表示 /app/catalog/** 不校验调用方 —— 内网自用可以,公网必须配
token: ''
# 远程模式的超时:目录读取不该拖垮安装流程,宁可快速失败
connect-timeout-ms: 3000
read-timeout-ms: 15000public-key 当前值对应 wujies/wujies-plugins/keys/private.pem(该目录已 gitignore)。正式环境必须自己生成一对:wujies/wujies-plugins/package.ps1 -Keygen,然后把新公钥贴进 public-key 并重建宿主 + 重启。公钥不是秘密;私钥只留在发布方。流程见 /package/signing。
⚠️ yml 里那段注释仍写着 keygen plugins/keys、对应 plugins/keys/private.pem —— 那是过时路径,wujies-platform/plugins/ 目录已经不存在(插件本体与密钥都在 wujies/wujies-plugins/)。
逐项:哪个值真的生效
| 配置项 | yml 里写的 | 注解兜底 | 容器里被覆盖成 |
|---|---|---|---|
deploy.plugin-dir | plugins-dist | plugins-dist | /app/plugins-dist |
deploy.frontend-dir | ../wujies-admin/app-market-dist/remote-apps | ../plus-ui/app-market-dist/remote-apps(与 yml 不一致) | /app/remote-apps |
security.mode | enforce | warn(与 yml 不一致) | 安装表单的 SECURITY_MODE,默认 warn |
security.public-key | 源码里的开发公钥 | 空串 | 无环境变量(只能改 yml + 重建镜像) |
security.allowed-frontend-extensions | 22 个扩展名 | 同一份列表 | 无环境变量 |
edition | official | official | official(服务端包)/ client(用户版包) |
catalog.mode | local | local | local / remote |
catalog.base-url | 空 | 空 | 客户端版由表单 CATALOG_BASE_URL 填 |
catalog.token | 空 | 空 | 表单 CATALOG_TOKEN(服务端预填随机值) |
catalog.connect-timeout-ms / read-timeout-ms | 3000 / 15000 | 3000 / 15000 | 无环境变量 |
plugin-runtime.enabled | yml 里没有这个键 | true | 无环境变量(见下,只能 -D) |
task.timeout-minutes | yml 里没有这个键 | 10 | 无环境变量 |
task.sweep-initial-delay-ms / sweep-interval-ms | yml 里没有这个键 | 30000 / 60000 | 无环境变量 |
task.* 三个键由 AppTaskTimeoutSweeper 读取:任务「多久没有进度」就判定为失败(判据是 update_time,不是开始时间),以及清扫器的首次延迟与间隔。要调就得自己往 yml 里加这三行 —— 现在只有注解兜底。
三处必须写清的口径差
deploy.frontend-dir:yml 与注解兜底指向两个不同的目录
| 位置 | 值 |
|---|---|
application.yml(实际生效) | ../wujies-admin/app-market-dist/remote-apps |
@Value 兜底(AppPackageDeployService、PluginAppInstaller) | ../plus-ui/app-market-dist/remote-apps |
两个值都是相对进程工作目录解析的(Paths.get(...).toAbsolutePath())。本机启动脚本 script/prune/start-host.ps1 会先把工作目录切到 wujies-platform/,所以 yml 那个值解析成工作区顶层的 wujies-admin/app-market-dist/remote-apps —— 那个 wujies-admin/ 目录里只有 app-market-dist/,没有 src/、没有 package.json。
⚠️ 别被名字骗到:宿主前端仓库是 plus-ui/,它的 package.json 里 name 恰好写着 wujies-admin(这是历史上误导过两次排查的根源)。plus-ui 的 dev server 与构建只认自己仓库根下的 app-market-dist/(plus-ui/vite/plugins/appMarketStatic.ts 里 path.resolve(root, 'app-market-dist'),URL 前缀只有 /app-runtime/ 与 /remote-apps/ 两个)。也就是说这两个目录是独立物理目录:后端默认写到工作区顶层,浏览器却从 plus-ui 取 —— 表现就是「安装成功、菜单也在,点开是远程视图不可用」。
本机想让 dev server / 构建直接读到后端落位的产物,把 frontend-dir 改成注解兜底那个值(../plus-ui/app-market-dist/remote-apps),然后重建宿主。容器化部署不走这条路:两边靠同一个卷对齐,见下面的覆盖表。
security.mode:同一个键有三个值
| 位置 | 值 |
|---|---|
application.yml | enforce |
注解兜底(PackageSecurityInspector、SignatureReadinessService) | warn |
1Panel 安装表单 SECURITY_MODE(data.yml 里 required: true、默认 warn) | warn |
compose 里是 APP_MARKET_SECURITY_MODE: ${SECURITY_MODE},环境变量优先级高于 yml —— 所以容器化部署装完默认是 warn,要 enforce 必须在安装表单(或装完去「参数」)显式改成 enforce。而 warn 是「检查并记录、照常安装」,属于为「存量包都还没签名」准备的过渡档。
结论:不要靠兜底值推断行为。确认实际值的两条路 —— 看部署/上传日志里的安全检查结论,或调 GET /app/console/signature-readiness(权限 app:console:list),返回里带 mode:
curl -H "Authorization: <你的登录 token>" \
http://<host>:<后端接口端口>/app/console/signature-readinesspublic-key 留空时,enforce 会拒绝所有包(包括你自己签的),报错里会明说原因。而这个键没有环境变量,只能改 yml + 重建镜像。三种模式的取舍见 /package/security-modes。
plugin-runtime.enabled:yml 里根本没有这个键
| 谁读它 | wujies-admin 的 PluginClassPathLoader(Spring 之前执行的启动加载器)与 PluginRuntimeManager 的 @Value |
| 怎么读 | System.getProperty("app-market.plugin-runtime.enabled", "true") |
| 代码默认值 | true(两处刻意保持一致) |
| 怎么改 | JVM 参数:-Dapp-market.plugin-runtime.enabled=false |
写进 yml 无效(启动加载器在 Spring 之前执行,只认系统属性)。写成环境变量 APP_MARKET_PLUGIN_RUNTIME_ENABLED 也不行 —— @Value 读得到,启动加载器读不到,于是变成最糟的组合:启动加载器让路(以为运行时接管了),运行时又没开 → 插件完全不加载。要用就用 -D。
| 机制 | 触发 | 开关为 true 时 |
|---|---|---|
PluginClassPathLoader(兼容) | 启动期扫插件目录挂到 TCCL | 让路,不加载 |
PluginRuntimeManager(默认) | 启动期 + 运行期,每插件独立类加载器 | 接管 |
⚠️ 容器里不能关这个开关:deploy/docker/backend/entrypoint.sh 只传 -Dapp-market.deploy.plugin-dir="${PLUGIN_DIR:-/app/plugins-dist}",没有 -Dloader.path(那个参数只在打包成 jar 后由 PropertiesLauncher 识别)。关掉运行时,容器里就既没有 loader.path 也没有插件运行时,插件一个都不会加载。
必须与别的组件对齐的三项
| 配置项 | 必须对齐到 | 不对齐的表现 | 怎么核对 |
|---|---|---|---|
deploy.plugin-dir | 本机:-Dloader.path(start-host.ps1 的 -PluginDir);容器:entrypoint.sh 的 -Dapp-market.deploy.plugin-dir(同一值域 /app/plugins-dist) | 后端把 jar 落到 A、加载器从 B 找 → 应用显示已启用但接口 404,「后端产物未加载」 | 看启动命令:start-host.ps1 会打印 java … -Dloader.path=…;容器里 docker exec <后端容器> printenv APP_MARKET_DEPLOY_PLUGIN_DIR |
deploy.frontend-dir | nginx /remote-apps/ 的 alias 物理路径(同一卷) | /remote-apps/<appCode>/index.js 404,远程视图不可用 | 打开 wujies-platform/deploy/docker/frontend/nginx.conf 看 location ^~ /remote-apps/ 的 alias;容器里核对 docker exec <后端容器> printenv APP_MARKET_DEPLOY_FRONTEND_DIR |
security.public-key | 发布方 wujies/wujies-plugins/keys/private.pem 对应的公钥 | 换钥只贴了新公钥、没重签旧包 → 已发布版本全部验签失败 | GET /app/console/signature-readiness 的分类计数(signed / unsigned / invalid / unverifiable);没配公钥时有签名的包会落到 unverifiable 而不是 invalid |
本机核对相对路径解析结果(工作目录必须是 wujies-platform/):
# 打印 frontend-dir 解析后的绝对路径
[System.IO.Path]::GetFullPath('..\wujies-admin\app-market-dist\remote-apps')nginx 侧与卷的完整对齐关系(四处路径 + 两行挂载)见 /deploy/frontend。
容器化部署覆盖了哪些键
1Panel 包内的 compose(deploy/1panel/apps/wujies-platform-server/5.6.3/docker-compose.yml 与 …-client/…)用环境变量覆盖 yml:
| 容器内变量 | 等价键 | 服务端包 | 用户版包 |
|---|---|---|---|
APP_MARKET_DEPLOY_PLUGIN_DIR | app-market.deploy.plugin-dir | /app/plugins-dist | /app/plugins-dist |
APP_MARKET_DEPLOY_FRONTEND_DIR | app-market.deploy.frontend-dir | /app/remote-apps | /app/remote-apps |
APP_MARKET_SECURITY_MODE | app-market.security.mode | 表单值,默认 warn | 同左 |
APP_MARKET_EDITION | app-market.edition | official(写死) | client(写死) |
APP_MARKET_CATALOG_MODE | app-market.catalog.mode | local(写死) | remote(写死) |
APP_MARKET_CATALOG_BASE_URL | app-market.catalog.base-url | 空串(写死) | 表单 CATALOG_BASE_URL |
APP_MARKET_CATALOG_TOKEN | app-market.catalog.token | 表单 CATALOG_TOKEN(面板预填随机值) | 表单 CATALOG_TOKEN,必须与服务端一致 |
三个必须记住的点:
edition与catalog.mode在容器里不是自由项:服务端包写死official+local,用户版包写死client+remote。用户版配local会直接启动失败并在报错里写明该改哪一行(MarketEditionStartupCheck),不是静默降级。见 /advanced/catalog-and-edition。public-key没有环境变量:容器的公钥来自镜像里的 jar。换公钥 = 改 yml → 重建wujies-admin.jar→ 重建后端镜像 → 重装/重启。只改面板参数改不到它。plugin-runtime.enabled在容器里同样只能靠-D,而 entrypoint 没有设置它(保持默认true)。关掉之前先读上一节。⚠️ 别指望用JAVA_OPTS追加:后端容器当前不读它 ——deploy/docker/backend/entrypoint.sh(deploy/dist/1panel/docker/backend/下的副本相同)最后一行是exec java -Dapp-market.deploy.plugin-dir="${PLUGIN_DIR:-/app/plugins-dist}" -jar /app/app.jar,没有引用$JAVA_OPTS;Dockerfile 里的JAVA_OPTS=""目前是死变量。要加 JVM 参数得改这一行或覆盖容器入口。
与应用市场相关的三处「排除名单」
配置被裁剪时,这三处最容易连带被删掉,删了就是运行期故障。
security.excludes 里的 /app/catalog/**
security:
excludes:
# …其它条目…
# 应用目录服务接口:服务间调用,没有本机会话。
# 已由 AppCatalogApiController 用共享令牌(app-market.catalog.token)兜底。
- /app/catalog/**/app/catalog/** 是服务间接口,调用方是另一个部署,没有本机登录会话,所以不能挂登录与权限注解。放行之后由 X-Catalog-Token 请求头兜底:服务端 catalog.token 为空 → 不校验调用方(内网自用可以,公网必须配);配了 → 比对请求头,不一致抛「目录接口令牌校验失败」。令牌是明文请求头,只防误连与扫描器,真正的防护是别把后端端口暴露到公网。
security.tenant.excludes 里应用市场相关的 6 张表
security:
tenant:
enable: true
excludes:
# …其余平台表(sys_menu / sys_tenant / sys_area / flow_spel 等)…
- app_info
- app_version
- app_category
- app_version_menu
- app_dependency
- app_plan| 表 | 为什么全局 | 漏配的后果 |
|---|---|---|
app_info / app_version / app_category | 应用目录是平台资产,所有租户看同一份 | 每个租户一份目录,上架一次只对自己可见;已发布版本查询也查不到 |
app_version_menu | 菜单归属映射(版本 → 菜单),菜单行本身也不分租户 | 菜单打不上「归属应用」标签,彻底删除时也清不干净 |
app_dependency / app_plan | 依赖关系与套餐定义都是目录的一部分 | 依赖解析在第二个租户下失效,套餐列表按租户分裂 |
不在此列的是安装侧数据:app_install、app_install_task、app_schema_history、app_subscription、app_audit_log 都带 tenant_id,必须按租户隔离 —— 它们记的是「这台部署的哪个租户装了什么、迁移执行到哪一步」。把它们加进 excludes,会让第二个租户装同一应用时重复执行 DDL。
xss.excludeUrls 里的 /app/console、/app/version、/app/package
xss:
enabled: true
excludeUrls:
# …其它条目…
# 应用市场:这些接口的 JSON 里合法地包含尖括号与富文本,
# 若不排除,XssHttpServletRequestWrapper 会把整个请求体当 HTML 清洗,
# 导致 "engine":">=5.6.2 <6.0.0" 这类版本约束把 JSON 拦腰截断
- /app/console
- /app/console/**
- /app/version
- /app/version/**
- /app/package
- /app/package/**版本兼容约束(engine 字段)、应用描述 / README 富文本、包清单字段里都合法地含 < 与 >。被当成 HTML 清洗的结果不是丢几个字符,而是把 JSON 截断,Jackson 报 Unexpected end-of-input: was expecting closing quote —— 表现像「上架表单点保存就报错」的前端 bug,实际是 XSS 过滤器动了请求体。
这些内容不经 XSS 清洗就落库。前端展示应用描述这类富文本时必须自己做安全处理(用已转义或白名单化的渲染组件),不要把原始 HTML 直接 v-html 出去。
未实现的配置项:deploy.c-frontend-dir
文档站另有页面(/advanced/remote-app 与 /reference/config)描述 C 端整站落位目录 app-market.deploy.c-frontend-dir 与 /apps-c/ 前缀。当前平台源码里没有这个键:application.yml 没有、wujies-app-market 下没有任何 @Value 读它、两个 1Panel 包的 compose 与 nginx 模板里也没有 /apps-c/ 这个 location。
按现状配置即可,别照那个键去改 —— 写了不生效(@Value 不存在),真正的落位只有 frontend-dir 一个根。
相关阅读
- /reference/config —— 全部配置项的逐项索引(源码位置 + 注解兜底 + 消费方)
- /deploy/backend —— 环境变量如何覆盖 yml、入口脚本的地址解析
- /deploy/frontend ——
frontend-dir与 nginx/remote-apps/的四处对齐 - /deploy/multi-deployment —— 多套部署与目录集中
- /advanced/catalog-and-edition ——
edition与catalog.mode的组合约束 - /advanced/plugin-runtime —— 两套插件加载机制为什么互斥
- /package/signing —— 密钥生成、签名与「就地重签」
- /package/security-modes ——
off/warn/enforce的取舍