白名单应仅包含认证前置接口如/login、/register、/captcha,按业务语义分组(如api.auth)并用except精准排除,避免全分组放行;中间件通过route()->option('allow_anonymous')识别,而非URL字符串匹配。

哪些接口该放进白名单?先想清楚放行逻辑
不是所有不需要鉴权的接口都适合进白名单——比如 /api/login、/api/register、/api/captcha 这类认证前置接口必须放行,但 /api/user/info 即使没登录也绝不能放。ThinkPHP 的路由分组拦截本质是「按前缀批量控制」,所以白名单应基于业务语义分组,而非随意罗列单个路由。
常见误操作:把整个 api.public 分组全放行,结果连 /api/public/admin/log 这种本该受限的路径也被绕过。正确做法是拆得更细,例如单独建 api.auth 分组放登录注册,再用中间件精准控制。
在路由分组里配中间件时,except 和 only 别混用
ThinkPHP 6+ 路由分组支持 middleware 参数传数组,但 except(排除)和 only(仅限)不能同时存在,否则会静默失效。白名单场景下推荐用 except,避免漏配。
-
except写法示例:Route::group('api', function () { /* 路由定义 */ })->middleware('checkAuth')->except(['login', 'register', 'captcha']); - 注意路径匹配是「后缀匹配」,
except里的login会同时匹配/api/login和/api/v2/login,如有歧义需改用完整路由名(如'api/login') - 若用
only,必须显式列出所有要鉴权的路由,一旦新增接口忘了加,就直接裸奔
checkAuth 中间件里怎么识别白名单路由?别只靠 $request->url()
单纯用 $request->url() 做字符串匹配容易出错——URL 可能带查询参数、大小写不一致、或被路由重写过。更可靠的方式是在路由定义阶段打标记:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
立即学习“PHP免费学习笔记(深入)”;
- 给白名单路由加
allow_anonymous属性:Route::post('login', 'LoginController@login')->option(['allow_anonymous' => true]); - 中间件里用
$request->route()->option('allow_anonymous')判断,比解析 URL 稳定得多 - 如果用的是资源路由(
Route::resource()),需对每个动作单独设置:Route::resource('captcha', 'CaptchaController')->only(['index'])->option(['allow_anonymous' => true]);
调试时发现白名单没生效?重点查这三处
白名单配置看似简单,但 ThinkPHP 的中间件执行顺序、路由缓存、以及闭包路由的匿名性会让问题难以定位:
- 中间件是否注册在
app/middleware.php且顺序正确?checkAuth必须在SessionInit之后、Cors之前 - 改完路由后没清缓存:
php think clear:route,否则旧规则还在内存里跑 - 闭包定义的路由(
Route::get('/', function(){}))无法通过option()打标,这类路由必须单独拎出来,用->middleware('allowAll')或直接不套分组
白名单真正难的不是写几行配置,而是让「谁该放行」这件事在团队协作中不模糊、不遗漏、不随路由重构而失效。每次加新接口,顺手检查它属于哪个分组、有没有被意外放行,比事后补漏洞成本低得多。


















