主题
租户配置
配置文件发布到 config/tenancy.php。项目通常只需要调整识别模式、中央域名、迁移范围、日志通道和队列监控。
识别模式
同一套部署全局选择一种模式:
| 模式 | 租户识别方式 | 部署要求 |
|---|---|---|
domain | 根据请求 Host 查询租户域名 | 为每个租户准备 Domain、DNS、TLS 和反向代理 |
shared_domain | 根据固定请求头 X-Tenant 查询租户 UUID | 中央后台与租户后台共用一个域名 |
独立域名模式
项目根目录 .env:
dotenv
TENANCY_IDENTIFICATION_MODE=domain
TENANT_CENTRAL_DOMAIN=admin.example.comweb/.env、web/.env.production 或当前构建环境文件:
dotenv
VITE_TENANCY_IDENTIFICATION_MODE=domainTENANT_CENTRAL_DOMAIN 应填写主机名,例如 admin.example.com,不包含协议和端口。后台租户资料中的 Domain 同样只填写主机名,例如 tenant-a.example.com。
生产环境创建租户时会检查租户域名的 A 或 CNAME 记录。本地与测试环境可以使用 nip.io 等开发域名:
text
tenant-a.127.0.0.1.nip.io请求进入租户域名后,asset() 会根据 tenancy.filesystem.asset_helper_tenancy 使用租户资产根地址;tenant_asset($path) 会显式生成命名路由 stancl.tenancy.asset 的地址。保持以下配置可注册 Stancl 租户资产路由:
php
'routes' => true,共享域名模式
项目根目录 .env:
dotenv
TENANCY_IDENTIFICATION_MODE=shared_domain
TENANT_CENTRAL_DOMAIN=admin.example.comweb/.env、web/.env.production 或当前构建环境文件:
dotenv
VITE_TENANCY_IDENTIFICATION_MODE=shared_domainTENANCY_IDENTIFICATION_MODE 控制后端租户识别中间件,VITE_TENANCY_IDENTIFICATION_MODE 控制管理端租户创建、域名字段和入口展示。两个值必须保持一致。
租户入口使用租户 UUID:
text
https://admin.example.com/#/login?tenant=<tenant-uuid>管理端在当前标签页的 sessionStorage 中保存租户 UUID,并为普通租户 API 注入:
http
X-Tenant: <tenant-uuid>
Authorization: Bearer <tenant-token>缺少 X-Tenant 的请求进入中央上下文。访问以下入口会清除当前标签页的租户 UUID:
text
https://admin.example.com/#/login?tenant=central租户 UUID、中央 Token 和各租户 Token 相互隔离:
- 当前标签页的租户 UUID 和注入 Header 保存在
sessionStorage。 - Token 保存在
localStorage,键名按central或租户 UUID 区分。 - 切换租户时会清除当前标签页的菜单标签,再读取目标上下文对应的 Token。
前端或其他客户端必须同时发送与当前租户匹配的 X-Tenant 和租户 Token。Header 已提供但租户 UUID 格式错误、租户不存在、停用或到期时,请求会返回租户不可用;Header 缺失时进入中央上下文。
管理端在构建时读取 VITE_TENANCY_IDENTIFICATION_MODE。修改该值后,重新启动开发服务或重新构建前端。共享域名模式创建租户时可留空 Domain,数据库名称按 UUID 生成,例如:
text
tenant_0190d8d7_1234_7abc_8def_1234567890ab前端配置
前端使用两个租户环境变量:
dotenv
VITE_TENANCY_MODE=true
VITE_TENANCY_IDENTIFICATION_MODE=shared_domainVITE_TENANCY_MODE=true 让管理端 API 使用当前页面源站的 /api 地址,安装命令会维护该值。VITE_TENANCY_IDENTIFICATION_MODE 只接受 domain 和 shared_domain,由部署人员在对应的前端构建环境文件中维护。
数据库与迁移
当前包只启用 MySQL 租户数据库管理器。中央连接沿用 DB_CONNECTION,租户上下文初始化后 Laravel 默认数据库连接会切换到对应租户库。
migration_parameters 用于手动执行 Stancl 租户迁移命令,例如 tenants:migrate:
php
'migration_parameters' => [
'--force' => true,
'--path' => [database_path('migrations/tenant')],
'--realpath' => true,
],global_migration_parameters 用于创建租户时,在租户上下文中执行 Laravel 全局迁移。默认配置会执行项目默认迁移路径:
php
'global_migration_parameters' => [
'--force' => true,
],如果项目根迁移中包含只属于中央库的表,应把所有租户公共迁移放到独立目录,并明确限制路径:
php
'global_migration_parameters' => [
'--force' => true,
'--path' => [database_path('migrations/tenant')],
'--realpath' => true,
],创建租户时还会按默认模块和套餐权限模块执行各模块迁移。
需要为某个租户使用已有 MySQL 实例时,先在 config/database.php 定义连接,再在租户管理页面选择该连接并填写对应数据库配置。
缓存与日志
租户缓存键使用 tenant:<uuid>: 前缀。缓存型 Session 会跟随租户缓存范围隔离。
默认隔离以下日志通道:
php
'logging' => [
'channels' => ['daily', 'single', 'query'],
'suffix_base' => 'tenant-',
],日志隔离只处理 logging.channels 中带 path 的文件通道,租户日志目录为:
text
storage/logs/tenant-{uuid}stack、stderr、syslog 等没有文件路径的通道仍按 Laravel 原配置工作。
队列监控
包配置默认关闭队列监控:
dotenv
TENANCY_QUEUE_MONITOR=falsecatch:tenant:install 会在根目录 .env 缺少该变量时追加 TENANCY_QUEUE_MONITOR=true;已有配置保持原值。修改后需要重启队列 worker,使监听器按新配置重新注册。
队列连接和 worker 用法参见租户队列。
文件系统与资产
默认隔离三个本地磁盘:
| 磁盘 | 租户目录 | 访问边界 |
|---|---|---|
uploads | storage/uploads/tenant-{uuid} | 公开业务上传,可通过 /uploads/tenant-{uuid}/... 访问 |
static | storage/static/tenant-{uuid} | 私有文件,不提供浏览器直连地址 |
certs | storage/certs/tenant-{uuid} | 私有证书,不提供浏览器直连地址 |
相关最小配置为:
php
'filesystem' => [
'suffix_base' => 'tenant-',
'disks' => ['uploads', 'static', 'certs'],
'root_override' => [
'uploads' => '%storage_path%',
'static' => '%storage_path%',
'certs' => '%storage_path%',
],
'suffix_storage_path' => true,
'asset_helper_tenancy' => true,
],
'routes' => true,执行以下命令创建公开上传软链接:
shell
php artisan storage:link软链接关系应为:
text
public/uploads -> storage/uploads上传接口保存并返回的相对路径类似:
text
uploads/tenant-{uuid}/2026-07-18/attachments/example.jpg页面统一使用 /uploads/tenant-{uuid}/... 地址。共享域名模式下,浏览器直接加载图片时不会自动携带 X-Tenant,因此公开业务上传应使用这类公开路径。uploads 目录不应保存密钥、证书或其他需要鉴权的文件。
独立域名模式可以继续使用 asset() 或 tenant_asset() 生成租户资产地址;后者依赖 stancl.tenancy.asset 命名路由。
资产排障
检查文件是否真实存在于
storage/uploads/tenant-{uuid}/...。检查
public/uploads是否正确指向storage/uploads。检查
tenancy.filesystem.disks和root_override是否包含目标磁盘。独立域名资产使用命名路由时,检查
tenancy.routes=true,并执行:shellphp artisan route:list --name=stancl.tenancy.asset修改配置或路由后重建缓存:
shellphp artisan optimize:clear php artisan config:cache php artisan route:cache
租户日志中的 Route [stancl.tenancy.asset] not defined 可能来自路由注册失败,也可能是缓存更新前留下的历史记录。确认命名路由存在后,继续观察是否产生新的同类日志。
PHP-FPM 请求生命周期
PHP-FPM 下,租户上下文会保持到响应发送、流式响应回调和 Kernel::terminate() 完成。租户初始化失败时,系统会恢复已经切换的中央数据库、缓存、文件系统和日志状态,再抛出原始初始化异常。