使用子域中间件匹配客户端工作区 Node+Express多企业平台开发需求
实现Node.js + Express多企业子域名隔离平台方案
我来给你梳理一下在Node.js + Express环境下实现多企业子域名专属平台的完整方案,从开发调试到部署落地都覆盖到:
1. 先搞定域名配置(线上+本地调试)
线上环境
你需要在域名服务商那里配置泛域名解析,把*.yourdomain.com指向你的服务器IP(或者负载均衡地址)。这样所有以企业标识开头的子域名(比如companyA.yourdomain.com、companyB.yourdomain.com)都会路由到你的服务器。
本地开发调试
为了在本地测试子域名功能,你需要修改系统的hosts文件:
- Windows:
C:\Windows\System32\drivers\etc\hosts - macOS/Linux:
/etc/hosts
添加类似这样的条目:
127.0.0.1 companyA.localhost 127.0.0.1 companyB.localhost
这样访问companyA.localhost:3000就可以模拟线上的子域名请求了。
2. Express子域名解析实现
这里有两种常用方式,选你觉得顺手的就行:
方式一:用现成的express-subdomain中间件
这是最省心的方式,专门处理子域名路由:
- 先安装依赖:
npm install express-subdomain --save
- 编写核心代码:
const express = require('express'); const subdomain = require('express-subdomain'); const app = express(); // 定义企业专属路由的处理逻辑 const companyRouter = express.Router(); // 子域名预处理中间件:提取企业标识并验证 companyRouter.use((req, res, next) => { // 从子域名数组里取第一个元素(比如companyA.localhost的子域名是['companyA']) const companyId = req.subdomains[0]; // 这里一定要加企业存在性验证!防止恶意子域名请求 // 示例:假设你有个checkCompanyExists函数查询数据库 // if (!await checkCompanyExists(companyId)) { // return res.status(404).send('企业不存在'); // } // 把企业ID挂载到req对象,后续所有路由都能直接用 req.companyId = companyId; next(); }); // 企业专属接口示例 companyRouter.get('/', (req, res) => { res.send(`欢迎访问${req.companyId}的专属平台!`); }); companyRouter.get('/orders', (req, res) => { // 这里查询数据库时自动带上req.companyId,实现数据隔离 // const orders = await getOrdersByCompanyId(req.companyId); res.send(`${req.companyId}的订单列表`); }); // 注册子域名路由:*匹配所有子域名 app.use(subdomain('*', companyRouter)); // 主域名路由(比如yourdomain.com或localhost) app.get('/', (req, res) => { res.send('欢迎来到主平台首页!'); }); app.listen(3000, () => { console.log('服务器运行在http://localhost:3000'); });
方式二:自己编写子域名解析中间件
如果你不想依赖第三方包,也可以自己实现解析逻辑:
const express = require('express'); const app = express(); // 自定义子域名解析中间件 app.use((req, res, next) => { const host = req.get('host'); // 拆分域名,处理带端口的情况(比如companyA.localhost:3000) const hostParts = host.split(':')[0].split('.'); let companyId = null; // 判断是否是子域名请求:比如localhost环境下是3段(companyA.localhost),线上是3段以上(companyA.yourdomain.com) if (hostParts.length >= 2 && (hostParts[1] !== 'com' || hostParts.length > 2)) { companyId = hostParts[0]; } // 同样要加企业验证逻辑 req.companyId = companyId; next(); }); // 路由里根据companyId区分处理 app.get('/', (req, res) => { if (req.companyId) { res.send(`欢迎访问${req.companyId}的专属平台!`); } else { res.send('主平台首页'); } }); app.get('/data', (req, res) => { if (!req.companyId) { return res.status(403).send('请通过企业子域名访问'); } // 业务逻辑:用req.companyId过滤数据 res.send(`${req.companyId}的专属数据`); }); app.listen(3000);
3. 数据隔离的核心注意事项
- 数据库层面强制过滤:所有企业相关的数据表都要加
company_id字段,所有查询、更新、删除操作必须带上company_id = req.companyId的条件,最好封装成数据库工具函数,避免开发者遗漏 - 权限验证前置:在子域名中间件里必须验证企业的合法性,防止攻击者用随机子域名访问系统
- 静态资源隔离:如果企业有自定义的静态资源(比如logo、样式),可以按企业ID分目录存储,比如
public/companyA/logo.png,然后通过路由返回对应资源 - 避免全局状态污染:不要用全局变量存储企业相关数据,所有数据都要通过
req.companyId动态获取
4. 部署阶段的关键配置
Nginx反向代理配置
如果用Nginx做反向代理,必须确保传递Host头,否则Express拿不到正确的子域名:
server { listen 80; server_name *.yourdomain.com yourdomain.com; location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; # 这行必须加! proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
HTTPS配置
要申请泛域名SSL证书(比如*.yourdomain.com),这样所有子域名都能使用HTTPS。Nginx的HTTPS配置示例:
server { listen 443 ssl; server_name *.yourdomain.com yourdomain.com; ssl_certificate /path/to/your/fullchain.pem; ssl_certificate_key /path/to/your/privkey.pem; location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; } }
内容的提问来源于stack exchange,提问作者Minoru
相关产品推荐
相关产品推荐

