#### 思路
- 多租户系统只部署一套h5页面,如何实现不同租户可通过不同域名访问各自的商城
- 租户在后台添加域名(入数据库)-->用户访问H5商城端-->获取当前url的域名-->通过接口(做redis缓存)获取该域名对应的租户ID-->给每个接口自动追加租户ID参数
- 后端逻辑:先修改nginx配置文件再重新加载配置文件
- 前端逻辑:通过当前访问域名获取租户ID,然后所有接口加租户ID访问
数据库表
joolun_plus_upms.h5_portal_domain
方案一:平台提供基础域名,不同租户通过追加前缀区分
实现步骤
- 以演示环境为例PC商城的基础域名为pro-pc.joolun.com
- 去你的域名服务商后台配置域名泛解析,将
*.pro-pc.joolun.com泛解析到你的服务器IP - 此种方式不直接https协议并且基础域名是一样的
- nginx配置
server {
# 公网入口禁止访问所有服务的管理端点
if ($uri ~* "(^|/)actuator(/|;|$)") {
return 404;
}
listen 80;
server_name *.pro-pc.joolun.com;
root /mnt/install/joolun-plus/h5-pc/;
location / {
try_files $uri $uri/ /index.html;
}
location ~* ^/(mall|mallapi|upms|code|live) {
proxy_pass http://127.0.0.1:9999;
#proxy_set_header Host $http_host;
proxy_connect_timeout 15s;
proxy_send_timeout 15s;
proxy_read_timeout 15s;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
- nacos后台配置基础域名:application-dev.yml
base:
#商城端h5默认域名配置
h5portal:
# h5移动端、pc端默认域名
domain:
pc: pro-pc.joolun.com
mobile: pro-mall.joolun.com
后台系统管理-->域名管理:系统会通过上面配置的基础域名自动生成一个当前租户的子域名,如:1.pro-pc.joolun.com,代表租户ID为1的租户PC商城访问域名
- 租户ID为1租户的PC商城访问地址则为:1.pro-pc.joolun.com
方案二:租户自备域名
- 方案一虽然实现了不同租户有不同访问域名,但基础域名是一样的,还需实现不同租户的域名可自由配置并支持https协议,原理和方案一一样
- 假如租户ID为1的租户要将其PC商城域名配置为:pro-pc.abc.com
- 先在该租户的域名服务商后台配置域名解析,将
pro-pc.abc.comCNAME记录类型解析到方案一生成的默认域名(1.pro-pc.joolun.com)下
- nginx配置,http、https协议
server {
# 公网入口禁止访问所有服务的管理端点
if ($uri ~* "(^|/)actuator(/|;|$)") {
return 404;
}
listen 80;
server_name pro-pc.abc.com;
root /mnt/install/joolun-plus/h5-pc/;
location / {
try_files $uri $uri/ /index.html;
}
location ~* ^/(mall|mallapi|upms|code|live) {
proxy_pass http://127.0.0.1:9999;
#proxy_set_header Host $http_host;
proxy_connect_timeout 15s;
proxy_send_timeout 15s;
proxy_read_timeout 15s;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
# 公网入口禁止访问所有服务的管理端点
if ($uri ~* "(^|/)actuator(/|;|$)") {
return 404;
}
listen 443 ssl;
server_name pro-pc.abc.com www.pro-pc.abc.com;
ssl_certificate /mnt/install/ssl/10392047_pro-pc.abc.com_nginx/pro-pc.abc.com.pem;
ssl_certificate_key /mnt/install/ssl/10392047_pro-pc.abc.com_nginx/pro-pc.abc.com.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
root /mnt/install/joolun-plus/h5-pc/;
location / {
try_files $uri $uri/ /index.html;
}
location ~* ^/(mall|mallapi|upms|code|live) {
proxy_pass http://127.0.0.1:9999;
#proxy_set_header Host $http_host;
proxy_connect_timeout 15s;
proxy_send_timeout 15s;
proxy_read_timeout 15s;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
以上两种方案合并后就实现了不同租户不同的访问域名,但需要维护人员手动修改nginx配置文件并重启nginx
- 我们还需将手动操作做成程序自动操作,也就是租户在后台配置完自己的域名后,程序自动加载新的nginx配置并生效
- 安装完nginx后打开
nginx.conf配置文件,可以看到nginx会加载/etc/nginx/conf.d/目录下所有.conf文件,该目录要配置到application-dev.yml中
- 为了维护的便捷性,我们将不同租户的配置文件分开,以租户ID命名,比如租户ID为1租户的nginx配置文件就为
/etc/nginx/conf.d/1.conf - nacos后台配置一些常量:application-dev.yml
base:
## 文件存放目录配置(用来存放微信支付证书,https证书)
home-dir:
windows: C:/joolun-file/
linux: /mnt/install/joolun-file/
## 存放租户nginx配置文件的目录,nginx会加载`/etc/nginx/conf.d/`目录下所有`.conf`文件,所以此处为`/etc/nginx/conf.d/`
nginx:
confDir: /etc/nginx/conf.d/
#前端h5相关配置
h5portal:
# 商城h5移动端、pc端默认域名,用于生成各租户的默认域名
domain:
pc: pro-pc.joolun.com
mobile: pro-mall.joolun.com
# 商城h5移动端、pc端打包目录,用于生成各租户nginx配置文件
path:
pc: /mnt/install/joolun-plus/h5-pc/
mobile: /mnt/install/joolun-plus/h5/
- 后台操作的其实是租户的nginx配置文件,比如开通一个http协议,则是在配置文件中添加了一条server
- 修改完nginx配置文件后台,系统会自动加载新的配置,通过执行CMD命令实现,不同系统cmd命令可能不同,可根据自己系统实际情况修改命令
核心代码
- 后端:
com/joolun/cloud/upms/common/util/NginxConfigUtil.java、com/joolun/cloud/upms/admin/controller/H5PortalDomainController.java - 前端:App.vue
租户域名 Actuator 防护与存量配置处理(2026-10-09 同步)
NginxConfigUtil 在新增 HTTP/HTTPS、删除域名或开启 HTTP 转 HTTPS 时,为当前租户文件内所有 server 补齐下面的规则。规则位于 server 的第一条指令,在 return 301 和业务代理之前执行;再次编辑不会重复追加同一规则。
server {
if ($uri ~* "(^|/)actuator(/|;|$)") {
return 404;
}
listen 80;
server_name tenant.example www.tenant.example;
# 原有 root、location 或 HTTPS 跳转配置保持不变
}
使用规范化后的 $uri,阻断 /actuator、/upms/actuator/env、/mallapi/ACTUATOR/health 和 /upms/actuator;参数/env,不拦截正常商城接口。HTTP 转 HTTPS 的 server 也必须先执行拦截,再执行 return 301。Nginx 规则顺序与变量定义见 rewrite 模块文档 和 $uri 文档。
本项目使用的 nginxparser 0.9.6 将读回的条件解析为 NgxIfBlock,但条件括号及 IF 输出存在缺陷。写入前统一转为带完整条件的普通块,确保后续新增、删除和协议转换不会生成缺失括号或花括号的 Nginx 配置;已有其他用途的 if 也保留。
自动生成逻辑在写入后先执行 nginx -t,检查成功才执行 nginx -s reload。检查或重载命令失败时恢复原文件并抛出错误,不再返回假成功。运行 UPMS 的系统账号必须拥有执行这些命令所需的配置、证书与 PID 文件权限;Nginx 如果使用自定义 -p 或 -c,检查与重载命令应同步指定同一实例。
部署新版 UPMS 后,新生成的配置自动带防护;部署本身不会自动扫描或修改存量 .conf。从未被域名管理改写过的租户文件,以及手工维护的主域名、泛域名、默认虚拟主机,仍需一次性补齐:
- 先备份
/etc/nginx/conf.d,备份应放在该加载目录以外,例如/root/nginx-conf-backup-20261008/。 - 对每个公网
server加入上面的if,放在已有return或rewrite之前;不要删除域名、证书、业务路由或跳转配置。 - 执行
nginx -t,检查成功后执行systemctl reload nginx;失败时先修复配置或用备份恢复,不执行 reload。 - 用每个租户域名分别验证 HTTP 和 HTTPS 的
/upms/actuator/env、/mall/actuator/refresh、/mallapi/actuator/loggers均返回 404,并确认商城页面和业务 API 正常。
nginx -t && systemctl reload nginx
curl -i http://租户域名/upms/actuator/env
curl -i -X POST http://租户域名/mall/actuator/refresh
curl -i https://租户域名/mallapi/actuator/loggers
这属于公网入口防护,Spring Boot Admin 继续直接通过受限内网访问服务端口。前提是 Gateway、微服务及 Nacos 端口没有公网映射且仅受信任主机可访问;若管理路径已自定义,需要同步调整拦截规则。
删除租户同步清理 Nginx(2026-10-09 同步)
删除单个域名与删除整个租户是不同流程。SysTenantServiceImpl.removeById 现在会清理被删除租户的独立文件 base.nginx.conf-dir/<租户ID>.conf,包含该租户全部 HTTP、HTTPS 与跳转规则,而不是逐个依赖当前管理员的域名记录或租户上下文。
处理顺序:
- 校验被删除租户 ID 为 1–19 位正整数;拒绝平台系统租户、路径穿越、符号链接与非普通文件。使用
Path.resolve拼接目录,配置目录末尾不必有/。 - 文件不存在时直接继续原租户删除流程,不执行 Nginx 命令;存在时先检查当前 Nginx 配置,再将文件移动为同目录的隐藏
.<租户ID>.conf-<随机值>.deleted临时备份。备份不以.conf结尾,不被现有conf.d/*.conf引入;移动保留原文件的所有者、权限等属性。 - 执行
nginx -t,通过后执行nginx -s reload。检查、移动或重载失败时中止后续租户数据删除,并恢复原配置;已尝试重载时同时尝试重新加载原配置,错误向上返回。 - 沿用 Plus 原有租户数据与 Redis 缓存清理流程,不引入 Pro 的分库删除和 Feign 远程清理。业务异常立即尝试补偿 Nginx;Spring 事务明确提交后删除临时备份,回滚时尝试恢复配置及重载。事务结果未知或补偿失败会输出错误日志,需要人工核对,不覆盖并发新建的配置。
仅删除目标租户的独立文件,不删除 joolun-plus.conf 等平台文件、共享泛域名或 SSL 证书,不递归删除配置目录。DNS/CNAME 记录不由这段流程管理;共享泛域名仍可能返回商城静态首页,不能仅凭首页是否能打开判断租户是否被删除。
运行 UPMS 的系统账号必须能移动、恢复和删除租户配置,并能执行 Nginx 检查和重载;未配置 base.nginx.conf-dir 时明确失败,不静默跳过。临时备份只在本次流程内用于补偿,不替代运维备份;进程中断遗留的 .deleted 文件应根据删除记录、数据库状态及日志人工决定恢复或清理。
边界:数据库删除与 Redis 缓存清理不具备跨系统原子事务。此次补齐 Nginx 补偿,不保证所有数据与缓存能一起回滚;失败仍需核对租户数据状态。 部署新版 UPMS 后,后续删除租户才使用这条流程,不会自动删除历史孤立配置。
回归测试使用临时目录和模拟依赖,不启动真实 Nginx,也不连接数据库或 Redis:
mvn -o -pl joolun-upms/joolun-upms-admin -am test-compile org.apache.maven.plugins:maven-surefire-plugin:3.2.5:test -Dtest=NginxConfigUtilTest,NginxTenantConfigRemovalTest,SysTenantNginxRemovalTest -Dsurefire.failIfNoSpecifiedTests=false
域名配置使用
- 假如需要使用pc.abc.com域名来访问PC商城,移动端h5同理
- 先在域名服务商后台将此域名解析到你的服务器IP
- 再到JooLun后台【系统管理】-【域名管理】-新增PC端商城域名
- 如需要https协议,则上传证书打开https即可
后台页面链接地址(下图)修改步骤
注意商户端h5的地址固定在配置文件 joolun-plus-ui/src/config/env.ts 中的h5HostShop变量值的设置

处理逻辑就是先取[域名管理]里面的默认域名,如果默认域名不存在就取 代码文件中 的域名







