ThinkPHP6灰度发布需贯穿请求入口、权限校验、响应出口三环节:入口处通过中间件读取X-Gray-Version头或配置默认值并存入上下文;权限校验时在SQL和缓存key中叠加gray_version条件;响应前回传X-Gray-Version头;插件按灰度版本隔离加载。

ThinkPHP6 实现授权灰度发布,核心是两件事:一是让请求“带身份”(即流量染色),二是让权限系统能识别并据此控制功能可见性或路由走向。它不是单纯加个中间件就完事,而是要贯穿请求入口、权限校验、响应出口三个环节。
一、在请求入口处完成灰度标识注入
灰度身份必须从上游来,比如网关透传的 X-Gray-Version 或用户登录态中携带的灰度标签。ThinkPHP6 不自动解析这些头,需主动读取:
- 在全局中间件(如 app/middleware.php 配置的
GrayAuthMiddleware)中,用$request->header('X-Gray-Version')获取值 - 若未提供,则 fallback 到配置项:
config('gray.default_version', 'prod') - 把该版本写入请求上下文,例如:
$this->app->session->set('gray_version', $version)或存入 Request 对象属性,供后续权限逻辑使用
二、改造权限校验逻辑,支持灰度维度
标准 RBAC 只认角色和权限名,灰度发布要求“同角色、不同灰度版本,看到的功能不同”。因此权限检查不能只查 sys_permission.name,还要叠加灰度条件:
- 在
checkPermission()方法中,查询权限时增加AND gray_version IN (?, ?)条件,支持'all'、'v1.2'、NULL(表示仅生产环境可见)等值 - 菜单渲染时,对
sys_permission.is_menu = 1的记录额外过滤:WHERE (gray_version IS NULL OR gray_version = ?) - API 接口权限中间件中,同样在 SQL 查询或缓存 key 中加入灰度版本,避免缓存污染(例如缓存 key 改为
permission_list_{$uid}_{$grayVersion})
三、在响应头中回传灰度标识
下游服务需要知道本次请求属于哪个灰度通道,所以必须在响应发出前写入染色 Header:
立即学习“PHP免费学习笔记(深入)”;
- 推荐在应用入口文件 public/index.php 最后一步设置:
$response = (new App())->run();$response->header('X-Gray-Version', $grayVersion);$response->send(); - 若使用中间件方式,确保该中间件排在所有业务中间件之后、且在
Response对象尚未发送前执行;否则会抛出headers already sent异常 - Header 名建议统一为
X-Gray-Version,值与权限表中字段一致,便于上下游对齐
四、配合插件架构实现灰度功能隔离
企业级场景下,灰度功能常以插件形式交付。ThinkPHP6 插件机制天然支持运行时启停,可进一步强化灰度控制:
- 将灰度特性封装为独立插件,放在 addons/user-center-v1.2/ 目录下,目录名体现灰度版本
- 插件的
addon.php中声明"gray_version": "v1.2",并在启用逻辑中校验当前请求的灰度版本是否匹配 - 路由绑定时,仅当
config('gray.version') === 'v1.2'才加载该插件的route/app.php



















