生产环境安全
本小节,汇总项目部署到公网生产环境前,需要检查的配置项、默认数据和网络边界。
适用范围
- 本文只覆盖项目自带的配置、初始化数据,以及 Nginx、Gateway 与端口的收口,不能代替代码审计、漏洞扫描、主机加固和依赖升级。
- 按本文完成配置,不代表系统已经安全,请结合自己启用的服务和部署方式检查。
- 文中的
<...>都是占位符,需要替换成你自己的值。不要把真实的密码、密钥提交到代码仓库或 Nacos 的公开命名空间。
# 1. 生产配置从哪里来?
每个服务都有自己的配置文件,例如 yudao-module-system/yudao-module-system-server/src/main/resources、yudao-gateway/src/main/resources:
| 文件 | 用途 | 生产注意 |
|---|---|---|
application.yaml | 所有环境共用 | 其中的密钥是示例值,需要在生产配置中覆盖 |
application-local.yaml | 本地开发 | system-server、infra-server 开启了 yudao.security.mock-enable 模拟登录,关闭了 yudao.captcha.enable 验证码,不能用于生产 |
application-dev.yaml | 开发、测试环境 | Actuator 全部开放;system-server 的 yudao.demo 为 true(演示模式) |
Nacos 中的 <服务名>-<profile>.yaml | 由 spring.config.import 加载 | 优先级高于本地文件,可以集中管理生产配置 |
推荐的做法:
① 为每个服务准备 prod 配置(本地的 application-prod.yaml,或 Nacos 中的 <服务名>-prod.yaml),再合并「2.1 示例」中的安全配置。项目没有跨服务的共享配置,每个启用的服务都需要配置。
② 启动时使用 --spring.profiles.active=prod 参数,或 SPRING_PROFILES_ACTIVE=prod 环境变量。
③ 密码、密钥使用 ${环境变量名} 引用,由启动脚本、Docker 或 K8s 的 Secret 注入。
# 2. 全局配置
# 2.1 示例
下面是业务服务(system-server、infra-server 等)prod 配置中与安全相关的片段。每个顶层配置项只出现一次,避免和已有内容重复。
springdoc:
api-docs:
enabled: false # 关闭 /v3/api-docs 接口(OpenAPI JSON)
swagger-ui:
enabled: false # 关闭 /swagger-ui 页面
knife4j:
enable: false # 关闭 Knife4j 增强
management:
endpoints:
web:
exposure:
include: health # 只通过 HTTP 暴露 health 端点
endpoint:
health:
show-details: never # 不返回数据库、Redis 等组件的详情
show-components: never
spring:
cloud:
nacos:
username: ${NACOS_USERNAME} # Nacos 开启鉴权后需要,见「5.3」
password: ${NACOS_PASSWORD}
datasource:
druid:
web-stat-filter:
enabled: false # 关闭 Web 统计
stat-view-servlet:
enabled: false # 关闭 /druid 控制台
mybatis-plus:
encryptor:
password: ${YUDAO_ENCRYPTOR_PASSWORD} # 见「2.6」:各服务保持一致,已有加密数据时不要随意更换
yudao:
captcha:
enable: true # 开启图片验证码(system-server)
security:
mock-enable: false # 关闭模拟登录
demo: false # 关闭演示模式(system-server 的 dev 配置为 true)
system-server 还需要配置短信验证码的范围:
yudao:
sms-code:
begin-code: 100000 # 短信验证码在 [begin-code, end-code] 之间随机
end-code: 999999
gateway-server 网关需要关闭 Knife4j 的聚合文档,并收紧 Actuator:
knife4j:
gateway:
enabled: false # 关闭 Knife4j 聚合文档
management:
endpoints:
web:
exposure:
include: health
# 2.2 Swagger、Knife4j 接口文档
| 配置项 | 位置 | 作用 |
|---|---|---|
springdoc.api-docs.enabled | 每个业务服务 | 关闭 /v3/api-docs 接口,即该服务全部接口的定义 |
springdoc.swagger-ui.enabled | 每个业务服务 | 只关闭 /swagger-ui 页面 |
knife4j.enable | 每个业务服务 | 关闭 Knife4j 的增强功能 |
knife4j.gateway.enabled | gateway-server | 只关闭聚合页面 |
注意,Gateway 为每个服务配置了 RewritePath=/admin-api/<模块>/v3/api-docs, /v3/api-docs 规则,例如 /admin-api/system/v3/api-docs 会被转发到 system-server 的 /v3/api-docs。并且各服务的 SecurityConfiguration 对 /v3/api-docs/** 设置了免登录。
所以,只关闭网关的聚合页面,不能阻止通过网关读取接口定义。需要在每个服务关闭 springdoc.api-docs.enabled,并在 Nginx 拦截这类路径,见「6.2」。
# 2.3 Actuator 监控端点
各服务、网关的 application-local.yaml、application-dev.yaml 中,management.endpoints.web.exposure.include 都是 '*',即开放所有端点。并且各业务服务对 /actuator/** 设置了免登录。
Actuator 是否生效,以启动日志中 Exposing ... beneath base path '/actuator' 这一行为准,也可以在服务所在的服务器上请求 /actuator 确认。
生产环境下,按「2.1 示例」只暴露 health。如果需要保留更多观测端点,必须保证服务端口只有内网可以访问,见「5. 端口与网络边界」。需要账号密码时,见「6.3」的 Nginx Basic 认证。
为什么不能直接配置 `spring.security.user` 账号密码?
业务服务使用的 YudaoWebSecurityConfigurerAdapter 自定义了基于访问令牌的 SecurityFilterChain,没有开启 HTTP Basic;各服务的 SecurityConfiguration 又对 /actuator、/actuator/** 设置了 permitAll()。所以只配置 spring.security.user.name、spring.security.user.password,不会保护 Actuator 端点。
如果希望由应用自己校验账号密码,需要为 /actuator/** 单独编写一个 SecurityFilterChain,并去掉上述 permitAll(),项目没有内置这个开关。不改代码时,可以使用「6.3」的网段限制 + Nginx Basic 认证。
Spring Boot Admin、Druid 有各自的账号密码,按「2.4」「2.5」配置即可。
management.endpoint.<id>.enabled 在 Spring Boot 3.4 起废弃,改用 management.endpoint.<id>.access。shutdown 端点默认就是关闭的,没有加入 exposure.include 时也不会通过 HTTP 暴露。
# 2.4 Druid 监控
各服务的 application-dev.yaml 中,spring.datasource.druid.stat-view-servlet.enabled 为 true,allow、login-username、login-password 都为空,并且各服务对 /druid/** 设置了免登录。
- 不需要时:按「2.1 示例」关闭
stat-view-servlet和web-stat-filter。filter.stat的慢 SQL 日志不受影响。 - 需要时:设置
login-username、login-password,并且只通过内网访问对应服务的端口。
# 2.5 Spring Boot Admin
- Server 端:内嵌在
infra-server中,默认没有启用。需要同时取消yudao-module-infra-server的pom.xml中spring-boot-admin-starter-server依赖的注释,以及AdminServerConfiguration类上@EnableAdminServer注解的注释。不需要时,保持现状即可。 - 登录认证:启用后,
AdminServerConfiguration为spring.boot.admin.context-path(默认/admin)下的路径注册了独立的认证:浏览器使用表单登录,Client 注册使用 HTTP Basic。账号、密码读取spring.boot.admin.client.username、spring.boot.admin.client.password,未配置时都是admin。 - Client 端:
spring.boot.admin.client.enabled: false只是停止向 Server 注册,不会关闭 Server 和/admin页面。
需要使用时:
① 修改上述账号、密码,通过环境变量注入。
② /admin 只允许内网或 VPN 访问。注意,Gateway 有一条 Path=/admin/** 的路由转发到 infra-server,所以公网 Nginx 不能把 /admin 转发给网关。
③ 修改 spring.boot.admin.frame-ancestors 为管理后台的域名。
④ 不要删除 spring.boot.admin.context-path 配置,AdminServerConfiguration 依赖它确定认证拦截的路径范围。
更多说明,可见 《服务监控》 文档。
# 2.6 登录、验证码与密钥
| 配置项 | 位置 | 生产要求 |
|---|---|---|
yudao.security.mock-enable | 各服务 | 必须为 false。开启后,以 yudao.security.mock-secret 开头的 token 可以模拟任意用户 |
yudao.captcha.enable | system-server | 开启。前端的 VITE_APP_CAPTCHA_ENABLE 需要保持一致 |
yudao.sms-code.begin-code、end-code | system-server 的 application.yaml | 默认两者相同,即固定验证码,改为一个范围 |
mybatis-plus.encryptor.password | 各服务的 application.yaml | 上线前替换,并在各服务保持一致。已有加密数据(例如 infra_data_source_config.password)后再更换,旧数据将无法解密 |
yudao.api-encrypt.* | system-server 等 | 默认关闭,启用时替换 request-key、response-key |
yudao.demo | system-server | 演示模式会拦截写操作。dev 配置为 true,生产按需设置 |
xxl.job.accessToken | 各服务的 application.yaml | 替换示例值,并与 XXL-Job 调度中心的 accessToken 一致,见「5.3」 |
免登录的接口,有三个来源:接口上的 @PermitAll 注解、yudao.security.permit-all_urls 配置项、各服务 SecurityConfiguration 中的 permitAll()。新增免登录接口时,需要接口自身完成签名或令牌校验。
# 3. 模块配置与数据库
只需要检查已启用的服务。
# 3.1 配置文件
| 服务 | 配置项 | 处理要求 |
|---|---|---|
| 通用 | spring.datasource.dynamic.datasource.*.username、password,spring.data.redis.password,MQ 的账号密码 | 使用独立账号和强密码,通过环境变量注入。Redis 生产需要开启密码 |
| system / member | justauth.type.*.client-id、client-secret,wx.mp.app-id、secret,wx.miniapp.appid、secret | 换成自己的应用,不用的三方登录直接删除 |
| pay | yudao.pay.order-notify-url、refund-notify-url、transfer-notify-url | 改为生产域名下的 HTTPS 地址,不要保留内网穿透地址 |
| trade | yudao.trade.express.* 中快递鸟、快递 100 的 key | 换成自己的密钥 |
| ai | spring.ai.*.api-key、yudao.ai.*.api-key,yudao.ai.midjourney.notify-url | 只开启用到的平台,通过环境变量注入;回调改为生产域名 |
| ai | spring.ai.mcp.server.enabled(默认 false) | 开启后,MCP 端点免登录,见「4」 |
| bpm | flowable.database-schema-update | 可选:表结构稳定后改为 false |
| iot | yudao.iot.gateway.token.secret | IoT 网关 application.yaml 中是示例值,替换为至少 32 位的随机值 |
| iot | yudao.iot.gateway.protocols 中各协议的 enabled、port,emqx 协议的 mqtt-username、mqtt-password,SSL 的 key-store-password、trust-store-password | 只开启用到的协议,替换示例账号密码 |
| iot | yudao.iot.gateway.rpc.url | 指向 iot-server 的内网地址 |
# 3.2 数据库与后台配置
以下数据来自初始化 SQL,建议在管理后台对应的页面修改。不要直接删除数据行,部分数据是系统运行所必需的。
其中 infra_config 是参数配置表,下表中它的几行是配置项的 config_key 值:按 config_key 找到对应的配置项,再修改它的 value。
| 表 | 字段 / 配置 key | 处理要求 |
|---|---|---|
system_users | username、password | 登录后修改内置账号(例如 admin)的密码,不用的演示账号停用 |
infra_config | system.user.init-password | 新建用户的初始密码,默认是弱口令,改为强密码 |
infra_config | system.user.register-enabled | 不需要后台自助注册时,改为 false |
infra_config | url.druid、url.spring-boot-admin、url.swagger、url.skywalking | 前端监控菜单 iframe 的地址,填内网地址或留空 |
system_oauth2_client | client_id、secret、redirect_uris、authorized_grant_types、status | client_id 为 default 的客户端是后台登录使用的,不要删除、禁用或改名。其它演示客户端不用时禁用,使用时更换 secret、收紧 redirect_uris |
system_social_client | client_id、client_secret、agent_id、public_key | 换成自己的应用 |
system_sms_channel | api_key、api_secret、callback_url | 换成自己的短信账号 |
system_mail_account | username、password、host、port、ssl_enable | 换成自己的邮箱,开启 SSL |
infra_file_config | config(JSON,包含存储的访问密钥) | 换成自己的存储配置 |
infra_data_source_config | url、username、password | password 由 mybatis-plus.encryptor.password 加密 |
pay_app | order_notify_url、refund_notify_url、transfer_notify_url | 改为生产域名 |
pay_channel | config(JSON,包含商户号、密钥、证书) | 换成自己的商户配置 |
mp_account | app_id、app_secret、token、aes_key | 换成自己的公众号 |
ai_api_key | api_key、url | 换成自己的密钥 |
# 4. 需要保留的业务入口
以下接口是业务必需的,不能一刀切封禁。它们大多在 /admin-api/ 下,经 Nginx、Gateway 转发即可。
| 路径 | 用途 | 鉴权方式 | Nginx |
|---|---|---|---|
/admin-api/pay/notify/order/{channelId}、refund/{channelId}、transfer/{channelId} | 支付渠道回调 | 免登录,由支付渠道的签名校验 | 随 /admin-api/ 转发 |
/admin-api/mp/open/{appId} | 公众号消息、服务器地址校验 | 免登录,校验微信签名 | 随 /admin-api/ 转发 |
/admin-api/ai/image/midjourney/notify | Midjourney 回调 | 免登录 | 启用 AI 时保留 |
/admin-api/infra/file/{configId}/get/** | 文件读取 | 免登录 | 保留。只有读取免登录,上传、删除等接口仍需登录和权限 |
/infra/ws | WebSocket,网关转发到 infra-server | 通过 ?token= 参数传递访问令牌 | 需要单独配置 Upgrade 头 |
/jmreport/**、/drag/**、/jimubi/** | 积木报表、仪表盘,网关转发到 report-server | Spring Security 放行,积木内部由 JmReportTokenServiceImpl 校验访问令牌 | 启用 report 服务时才转发,并参考积木官方的 《生产安全》 (opens new window) 文档 |
/sse、/mcp/message | MCP Server,网关转发到 ai-server | 免登录 | 未启用时不转发;启用时只对受信的客户端开放 |
| IoT 网关的协议端口 | 设备接入 | 由各协议的设备认证完成 | 不经过 Nginx 和 Gateway,按启用的协议开放端口 |
WebSocket 的访问令牌在 URL 参数中,会出现在 Nginx 的 access log 里,注意控制日志文件的访问权限。
# 5. 端口与网络边界
# 5.1 请求链路
浏览器 / App ──443──> Nginx ──内网──> gateway-server(48080)──内网──> 各业务服务(48081、48082 ...)
设备 ──────────────按启用的协议端口──> IoT 网关 ──内网 /rpc-api/iot/**──> iot-server
| 端口 | 服务 | 公网 |
|---|---|---|
| 80、443 | Nginx | 开放 |
| 48080 | gateway-server | 不开放,只允许 Nginx 访问 |
| 48081 及以上 | 各业务服务 | 不开放,只允许网关和服务之间访问 |
| 8848、9848 等 | Nacos | 不开放 |
| XXL-Job 调度中心、Spring Boot Admin、SkyWalking 等 | 基础服务 | 不开放,通过内网或 VPN 访问 |
| 3306、6379、MQ 等 | 中间件 | 不开放 |
| IoT 协议端口(例如 HTTP 8092、MQTT 1883) | IoT 网关 | 按启用的协议开放 |
友情提示:Docker 的端口映射
使用 docker run -p 48081:48081 时,Docker 会直接修改 iptables,可能绕过 ufw、firewalld 等主机防火墙规则。建议不映射业务服务的端口,或写成 -p 127.0.0.1:48081:48081,并在云厂商的安全组中限制。
# 5.2 为什么业务服务不能被直接访问?
关闭 Swagger、Actuator 只能减少暴露的接口,不能代替网络隔离。原因是服务之间存在“信任内网”的设计:
① 内部身份头:网关校验访问令牌后,把登录用户放到 login-user 请求头中转发给服务。服务的 TokenAuthenticationFilter 会优先信任这个请求头,不再校验令牌。网关会先清除客户端传入的 login-user、data-permission-enable 请求头,但直接访问服务端口时,没有这一步。
② RPC 接口:各服务的 SecurityConfiguration 对 /rpc-api/** 设置了免登录,供 Feign 在服务之间调用。
③ 网关不拦截未登录请求:网关的 TokenAuthenticationFilter 在没有令牌时,仍然继续转发,是否需要登录由服务自己判断。所以网关自身也不能直接暴露,而且 Nginx 只转发需要的路径。
因此,部署时需要保证:业务服务、网关的端口,只能从内网访问。
# 5.3 Nacos 与 XXL-Job
- Nacos:开启
nacos.core.auth.enabled,并替换nacos.core.auth.plugin.nacos.token.secret.key、nacos.core.auth.server.identity.key、nacos.core.auth.server.identity.value,设置方式参见 《Docker 部署》 的「1.5 安装 Nacos」。然后在各服务配置spring.cloud.nacos.username、password。服务要先用这组账号密码连接 Nacos,才能拉取远程配置,所以它们必须写在本地配置文件,或通过环境变量提供,不能只写在 Nacos 的远程配置里。Nacos 中保存了各服务的配置,包括数据库密码,控制台不能对公网开放。 - XXL-Job:
xxl.job.accessToken用于调度中心和执行器之间的通信校验,替换示例值,两边保持一致。执行器端口(xxl.job.executor.port)和调度中心都只在内网访问,并修改调度中心自己的登录账号密码。
# 6. Nginx 与 HTTPS
# 6.1 注意点
- Nginx 匹配 location 的优先级是:
=精确匹配,然后是带^~的最长前缀,然后按书写顺序匹配正则,最后才是普通前缀。拦截规则使用正则时,必须写在/admin-api/等转发规则之前。 - 被拦截的路径,如果没有命中任何转发规则,会落到前端的
try_files ... /index.html兜底,返回 200 和前端页面。显式返回 404,才能在「7. 上线检查」时明确区分。 - 不要使用
location /直接转发到网关,否则网关的/admin/**、/jmreport/**、/sse等路由,以及网关自己的/actuator都会暴露。
# 6.2 示例
在 《Docker Compose 部署》 的 nginx/default.conf 基础上,增加 HTTPS 和拦截规则:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name <你的域名>;
return 301 https://$host$request_uri; # HTTP 跳转 HTTPS
}
server {
listen 443 ssl;
server_name <你的域名>;
ssl_certificate /etc/nginx/cert/<证书文件>.pem; # 证书申请,见《HTTPS 证书》
ssl_certificate_key /etc/nginx/cert/<证书文件>.key;
root /usr/share/nginx/html; # 前端 dist 目录
index index.html;
client_max_body_size 32m; # 与后端 spring.servlet.multipart.max-request-size 保持一致
# ① 拦截:网关为每个服务重写的接口文档路径,例如 /admin-api/system/v3/api-docs
location ~ ^/(admin-api|app-api)/[^/]+/v3/api-docs {
return 404;
}
# ② 拦截:管理端点
location = /doc.html { return 404; }
location ~ ^/(actuator|druid|admin|swagger-ui|v3/api-docs|webjars|rpc-api)(/|$) {
return 404;
}
# ③ 管理后台 API、用户 App API。支付、公众号回调和文件读取都在其中,不要单独拦截
location ~ ^/(admin-api|app-api)/ {
proxy_pass http://<网关内网地址>:48080;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr; # 最外层 Nginx 使用 $remote_addr 覆盖,见「6.4」
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header login-user ""; # 不转发客户端传入的内部身份头(网关也会清除)
proxy_set_header Connection "";
proxy_buffering off; # 流式响应(SSE)不缓冲
proxy_read_timeout 300s;
}
# ④ WebSocket,网关转发到 infra-server
location ^~ /infra/ws {
proxy_pass http://<网关内网地址>:48080;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header login-user "";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
}
# ⑤ 按需:启用积木报表时,才取消注释
# location ~ ^/(jmreport|drag|jimubi)/ {
# proxy_pass http://<网关内网地址>:48080;
# proxy_set_header Host $http_host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $remote_addr;
# proxy_set_header login-user "";
# }
# ⑥ 前端 history 路由的兜底
location / {
try_files $uri $uri/ /index.html;
}
}
# 6.3 内网访问管理面
如果需要使用 Spring Boot Admin、Swagger 等管理页面,可以增加一个只监听内网地址的 server,安全组不对公网开放该端口:
server {
listen <内网 IP>:8081; # 只监听内网地址
allow <VPN 或办公网段>; # 例如 10.8.0.0/24
deny all;
# 网关自己的 Actuator:在网段限制之外,再加一层 Nginx Basic 认证
location ^~ /actuator {
auth_basic "actuator";
auth_basic_user_file /etc/nginx/.htpasswd-actuator; # 不要提交到代码仓库
proxy_pass http://<网关内网地址>:48080;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Authorization ""; # 不把 Basic 认证头传给后端
}
# 业务服务的 Actuator:网关不会转发,需要直接指向服务的内网端口
# 例如 /system-actuator/health -> system-server 的 /actuator/health,其它服务按需复制
location ^~ /system-actuator/ {
auth_basic "actuator";
auth_basic_user_file /etc/nginx/.htpasswd-actuator;
proxy_pass http://<system-server 内网地址>:48081/actuator/;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Authorization "";
}
# Spring Boot Admin、Swagger 等页面,经网关转发,使用各自的账号密码登录
location / {
proxy_pass http://<网关内网地址>:48080;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
htpasswd 文件在服务器上创建,执行后按提示输入密码,密码不会留在命令历史里:
# htpasswd 命令来自 httpd-tools(CentOS)或 apache2-utils(Ubuntu)
htpasswd -c /etc/nginx/.htpasswd-actuator <用户名> # -c 表示新建文件,只在首次创建时使用
chown root:<Nginx 进程用户组> /etc/nginx/.htpasswd-actuator # 常见为 nginx 或 www-data
chmod 640 /etc/nginx/.htpasswd-actuator # 只允许 root 和 Nginx 进程用户组读取
<Nginx 进程用户组> 按 Nginx worker 进程实际的运行组填写,可以查看 nginx.conf 中的 user 指令。如果该组读不到文件,即使密码正确,Nginx 也会返回 500。以后新增账号时,去掉 -c 参数,例如 htpasswd /etc/nginx/.htpasswd-actuator <新用户名>,否则会覆盖已有的账号。
几个注意点:
- 网段限制仍然是主要的保护手段。
allow、deny和auth_basic同时存在时,默认两者都要满足,Basic 认证只是多加一层,不能因此把网关、业务服务或这个端口开放到公网。 - 网关的
/actuator只是网关自己的端点。网关的业务路由只转发/admin-api/<模块>/**等路径,业务服务的/actuator不会经网关转发,所以示例单独指向system-server的内网端口。 - Basic 认证的账号密码只做了 Base64 编码,必须通过 VPN 访问,或者为这个
server配置 HTTPS。 - 通过该入口拉取指标的监控客户端,例如 Prometheus,需要配置同样的账号密码(Prometheus 的
basic_auth)。 - 不要在
/admin上叠加 Nginx Basic 认证:Spring Boot Admin Client 注册时,也使用 HTTP Basic 的Authorization请求头,两者会冲突。 - Spring Boot Admin、Druid 仍然需要配置各自的账号密码。Druid 控制台不经过网关,需要直接访问对应服务的内网端口。
# 6.4 客户端 IP
后端通过 X-Forwarded-For、X-Real-IP 等请求头获取客户端 IP,用于登录日志、操作日志等。最外层 Nginx 如果使用 $proxy_add_x_forwarded_for,会保留客户端自己传入的值,导致 IP 可以伪造。所以:
- Nginx 是最外层时,使用
proxy_set_header X-Forwarded-For $remote_addr;覆盖。 - Nginx 前面还有 SLB、CDN 时,使用
set_real_ip_from <SLB 网段>;和real_ip_header X-Forwarded-For;,让$remote_addr变成真实的客户端 IP。allow、deny也依赖它。
# 7. 上线检查
# 7.1 公网侧
在公网的机器上执行:
DOMAIN=https://<你的域名>
for p in /actuator /actuator/env /druid/index.html /admin /admin/applications \
/v3/api-docs /doc.html /admin-api/system/v3/api-docs /admin-api/infra/v3/api-docs \
/rpc-api/system/user/get; do
printf '%-34s %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' "$DOMAIN$p")"
done
预期都是 404。如果返回 200,并且内容是前端的 index.html,说明请求落到了 SPA 的兜底,没有命中拦截规则,需要检查 location 的写法。
再执行 curl -s $DOMAIN/admin-api/system/user/profile/get(不携带访问令牌)。该接口需要登录,预期返回 code 为 401 的 JSON,例如 {"code":401,"data":null,"msg":"账号未登录"},说明 Nginx 和网关的转发都正常。如果返回前端的 HTML 页面,或网关未匹配到路由的错误(例如 404),都不算通过。
最后,从公网检查网关、业务服务、Nacos 的端口,例如 nc -zv -w 3 <公网 IP> 48081、nc -zv -w 3 <公网 IP> 8848,预期连接失败。
# 7.2 内网侧
在服务所在的机器上执行:
curl -s http://127.0.0.1:48081/actuator # 只应包含 health;未引入 Actuator 时,返回未找到
curl -s http://127.0.0.1:48081/v3/api-docs | grep -c '"openapi"' # 预期为 0
curl -s http://127.0.0.1:48080/actuator # 网关同样只应包含 health
服务对不存在的路径,可能返回 HTTP 200,但 JSON 中的 code 为 404。请以响应内容判断,不要只看状态码。
如果启用了 Spring Boot Admin,从 VPN 访问 /admin,确认需要输入账号密码。登录 Nacos 控制台,确认需要账号密码。
# 7.3 业务回归
- 管理后台、用户 App 可以正常登录,登录需要图片验证码。
- 短信验证码是随机值,原来的固定验证码无法通过。
- 支付渠道的沙箱回调可以到达,订单状态正常变化。
- 公众号的服务器地址校验可以通过。
- WebSocket 可以连接,文件链接可以打开。
- XXL-Job 的定时任务可以正常调度。
- 启用积木报表时,登录后可以预览报表;未登录时无法访问报表数据。
- 启用 IoT 时,设备可以通过开放的协议端口接入。