50ad577b29
- 根因:浏览器对**同源**的 POST/PUT/DELETE 也会带 Origin,而 Spring MVC 只看 "请求里有没有 Origin"(Spring 7.0.9 的 DefaultCorsProcessor 没有同源短路), 带了又不在白名单里 → 进 Controller 之前直接 403 - WebConfig 的白名单写死为 localhost/内网;注释声称"生产配置见 application-prod.yml 的 fill.cors.allowed-origins",但该属性根本不存在、也没人读它 —— 于是"前后端同源部署"的登录/上传/提交必然登不上(生产 2026-10-20 事故) - 改为配置驱动:FillProperties.Cors + fill.cors.allowed-origins (可用 XT_CORS_ALLOWED_ORIGINS 覆盖);prod 默认 shenxue.jxit.com / admin.shenxue.jxit.com / *.shenxue.jxit.com; dev 补上花生壳穿透域名(H5 走穿透时 POST 同样会被拦) - 启动日志打印生效白名单,排障先看那一行 - 新增 CorsWhitelistTest(3 条):生产 yml 真能绑定、通配符匹配租户子域、陌生源仍被拒 - docs/09 §2.4:本站地址必须在白名单 + 反代必须传 Host/X-Real-IP/X-Forwarded-For/X-Forwarded-Proto(缺了登录日志 IP 全是 127.0.0.1); 交接摘要 §一·四十四 记录事故,并写清"1700+ 条 e2e 全绿为什么还是漏了" 为什么 e2e 没抓到:脚本是 Python requests,不带 Origin 头,永远走不到 CORS 分支; 本地浏览器用的 localhost 正好命中写死的 dev 白名单。唯一能暴露它的是"浏览器 + 正式域名"。 验证:生产白名单下 admin / 主域名 / t1 / t12 → 200,evil / 后缀拼接 / http → 403; 真实 RSA 登录 + Origin: https://admin.shenxue.jxit.com → 200、code=0、拿到 token; mvn test 15/15;verify-all --quick 6/6;check-test-profile 通过。 接口、库表、契约均无变化。
81 lines
3.9 KiB
Bash
81 lines
3.9 KiB
Bash
# ═══════════════════════════════════════════════════════════
|
||
# 生产环境变量模板(prod)
|
||
#
|
||
# 用法:
|
||
# cp deploy/prod.env.example deploy/prod.env
|
||
# chmod 600 deploy/prod.env
|
||
# # 按下面命令生成四个密钥并填进去,再填 PG / REDIS / QINIU / WX
|
||
# set -a; . ./deploy/prod.env; set +a
|
||
# java -jar xt-server.jar --spring.profiles.active=prod
|
||
#
|
||
# 1Panel:把每一条粘到「应用 → 环境变量」,**值不要带引号**。
|
||
#
|
||
# ⚠️ deploy/prod.env 已 gitignore。不要把真值写回本文件。
|
||
# ⚠️ 2026-10 曾把 1Panel 的库/Redis 密码直接写进 application-prod.yml
|
||
# 并提交。那些值应视为已泄露:要么轮换,要么确认仓库只有可信的人能看。
|
||
# ═══════════════════════════════════════════════════════════
|
||
|
||
# ── PostgreSQL ────────────────────────────────────────────
|
||
# 1Panel 里通常就是容器名;换成你的实例地址
|
||
PG_HOST=
|
||
PG_PORT=5432
|
||
PG_DB=
|
||
PG_USER=
|
||
PG_PASSWORD=
|
||
|
||
# ── Redis ─────────────────────────────────────────────────
|
||
REDIS_HOST=
|
||
REDIS_PORT=6379
|
||
REDIS_PASSWORD=
|
||
|
||
# ── 四个密钥(必填,ProdEnvCheck 缺一个就拒绝启动)─────────
|
||
#
|
||
# XT_JWT_SECRET openssl rand -base64 64 | tr -d '\n'
|
||
# XT_AES_KEY openssl rand -base64 32 | tr -d '\n'
|
||
# XT_HMAC_KEY openssl rand -base64 48 | tr -d '\n'
|
||
# XT_RSA_PRIVATE_KEY openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out prod-rsa.pem
|
||
# openssl pkcs8 -topk8 -nocrypt -in prod-rsa.pem -outform DER | base64 -w0
|
||
#
|
||
# ⚠️ XT_AES_KEY 一旦用来加密过证件号就不能直接换(旧密文解不开且不报错)。
|
||
XT_JWT_SECRET=
|
||
XT_AES_KEY=
|
||
XT_HMAC_KEY=
|
||
XT_RSA_PRIVATE_KEY=
|
||
|
||
# ── 对象存储(生产必须 qiniu)──────────────────────────────
|
||
XT_STORAGE_PROVIDER=qiniu
|
||
QINIU_ACCESS_KEY=
|
||
QINIU_SECRET_KEY=
|
||
QINIU_BUCKET=
|
||
QINIU_DOMAIN=
|
||
QINIU_REGION=auto
|
||
# 启动时真的走一遍 写入→存在→读回→删除;密钥/空间配错会当场暴露
|
||
XT_STORAGE_SELF_CHECK=true
|
||
# false = 只保留压缩后的规范件(默认)
|
||
XT_STORAGE_KEEP_RAW=false
|
||
|
||
# ── 微信:小程序 ──────────────────────────────────────────
|
||
WX_MP_APP_ID=
|
||
WX_MP_APP_SECRET=
|
||
WX_MP_PUBLIC_BASE_URL=
|
||
WX_MP_ENV_VERSION=release
|
||
|
||
# ── 微信:公众号网页授权(H5)─────────────────────────────
|
||
# 回调与页面都必须是 https 正式域名,且与 H5 站点同源
|
||
WX_H5_APP_ID=
|
||
WX_H5_APP_SECRET=
|
||
WX_H5_CALLBACK=
|
||
WX_H5_BASE_URL=
|
||
|
||
# ── 其他 ─────────────────────────────────────────────────
|
||
XT_LOG_DIR=/var/log/xt
|
||
# 按域名区分单位时才打开
|
||
XT_TENANT_DOMAIN_ENABLED=false
|
||
XT_TENANT_DOMAIN_BASE=
|
||
# CORS 白名单(逗号分隔,Spring allowedOriginPatterns 语义)。
|
||
# ⚠️ **本站自己的地址也必须列上**:浏览器给同源的 POST 也带 Origin,
|
||
# 不在名单里 → 登录/上传一律 403「Invalid CORS request」(2026-10-20 生产事故)。
|
||
# PC 前端在 https://admin.shenxue.jxit.com,就是下面这行的默认值
|
||
# (application-prod.yml 里已经写了同样的默认,这里只是让它可运维覆盖)。
|
||
XT_CORS_ALLOWED_ORIGINS=https://shenxue.jxit.com,https://admin.shenxue.jxit.com,https://*.shenxue.jxit.com
|