如何通过ThinkPHP中间件设置特定域名访问限制?
- 内容介绍
- 文章标签
- 相关推荐
本文共计907个文字,预计阅读时间需要4分钟。
ThinkPHP 6 的中间件中,可以使用 `$request` 对象来获取请求信息。以下是使用 `$request` 的基本示例:
- 推荐用
$request->domain()(TP6.1+),它自动剥离端口、转小写、处理 IP 地址,返回干净域名 - 如果用
$request->header('host'),要手动explode(':', ...)[0]去端口,否则localhost:8000和localhost会被当成两个不同域名 - 本地开发时,浏览器常通过
127.0.0.1或localhost访问,但服务器部署后真实域名才生效,测试务必在同环境验证
如何配置白名单域名并做精确匹配
硬编码域名列表容易出错,建议把白名单写进配置文件(如 config/app.php 加 'allowed_domains' => ['api.example.com', 'admin.example.com']),中间件里读取后做严格比对。别用 strpos() 或模糊匹配——example.com 会误放行 badexample.com。
- 用
in_array($request->domain(), config('app.allowed_domains'))做完全匹配 - 如果需要支持子域名通配(如
*.example.com),得自己写判断逻辑:str_ends_with($host, '.example.com') || $host === 'example.com' - 注意
www.example.com和example.com是两个域名,白名单里必须都写上,或者统一重定向到主域名
拦截失败时返回什么状态码和响应
直接 return response('', 403) 虽然能拦住,但前端可能收不到明确提示;返回 JSON 更利于前后端协作。但别返回 404——这不是资源不存在,而是权限拒绝。
- 生产环境用
return json(['code' => 403, 'msg' => 'Forbidden domain'])->code(403) - 避免返回完整错误堆栈(尤其开启调试模式时),防止暴露服务器信息
- 如果项目用了 CORS,记得在中间件里提前设置响应头(
$response->header('Access-Control-Allow-Origin', '*')),否则跨域请求可能卡在预检阶段就失败,让你误以为拦截没生效
为什么有些请求绕过了域名检查
常见漏网原因不是代码写错,而是中间件注册位置不对。ThinkPHP 的中间件执行顺序很关键:如果域名检查中间件放在路由之后(比如在 app/middleware.php 里写在了 AllowCrossDomain::class 后面),某些静态资源或未定义路由可能根本不会走到你这步。
立即学习“PHP免费学习笔记(深入)”;
- 必须把域名拦截中间件加到全局中间件数组开头(
app/middleware.php的$middleware第一项) - 确认没在控制器或路由闭包里用
->middleware([])覆盖掉全局中间件 - CLI 请求(如命令行运行任务)没有
Host头,$request->domain()返回空字符串,白名单里没配空值就会被拦——需单独处理:if (empty($host)) { return $next($request); }
本文共计907个文字,预计阅读时间需要4分钟。
ThinkPHP 6 的中间件中,可以使用 `$request` 对象来获取请求信息。以下是使用 `$request` 的基本示例:
- 推荐用
$request->domain()(TP6.1+),它自动剥离端口、转小写、处理 IP 地址,返回干净域名 - 如果用
$request->header('host'),要手动explode(':', ...)[0]去端口,否则localhost:8000和localhost会被当成两个不同域名 - 本地开发时,浏览器常通过
127.0.0.1或localhost访问,但服务器部署后真实域名才生效,测试务必在同环境验证
如何配置白名单域名并做精确匹配
硬编码域名列表容易出错,建议把白名单写进配置文件(如 config/app.php 加 'allowed_domains' => ['api.example.com', 'admin.example.com']),中间件里读取后做严格比对。别用 strpos() 或模糊匹配——example.com 会误放行 badexample.com。
- 用
in_array($request->domain(), config('app.allowed_domains'))做完全匹配 - 如果需要支持子域名通配(如
*.example.com),得自己写判断逻辑:str_ends_with($host, '.example.com') || $host === 'example.com' - 注意
www.example.com和example.com是两个域名,白名单里必须都写上,或者统一重定向到主域名
拦截失败时返回什么状态码和响应
直接 return response('', 403) 虽然能拦住,但前端可能收不到明确提示;返回 JSON 更利于前后端协作。但别返回 404——这不是资源不存在,而是权限拒绝。
- 生产环境用
return json(['code' => 403, 'msg' => 'Forbidden domain'])->code(403) - 避免返回完整错误堆栈(尤其开启调试模式时),防止暴露服务器信息
- 如果项目用了 CORS,记得在中间件里提前设置响应头(
$response->header('Access-Control-Allow-Origin', '*')),否则跨域请求可能卡在预检阶段就失败,让你误以为拦截没生效
为什么有些请求绕过了域名检查
常见漏网原因不是代码写错,而是中间件注册位置不对。ThinkPHP 的中间件执行顺序很关键:如果域名检查中间件放在路由之后(比如在 app/middleware.php 里写在了 AllowCrossDomain::class 后面),某些静态资源或未定义路由可能根本不会走到你这步。
立即学习“PHP免费学习笔记(深入)”;
- 必须把域名拦截中间件加到全局中间件数组开头(
app/middleware.php的$middleware第一项) - 确认没在控制器或路由闭包里用
->middleware([])覆盖掉全局中间件 - CLI 请求(如命令行运行任务)没有
Host头,$request->domain()返回空字符串,白名单里没配空值就会被拦——需单独处理:if (empty($host)) { return $next($request); }

