webman权限验证必须拆分认证与授权,用链式中间件+显式上下文传递;禁止在构造函数访问请求数据,须在handle中通过$request->getattribute('user')获取身份,并配合缓存+降级双保险机制保障高并发下的权限校验稳定性。

Webman中间件做权限验证,不能靠“先登录再查权限”这种线性思维——它没内置 Auth 管理器,$_SESSION 在长连接或 CLI 模式下不可靠,直接读 $request->cookie() 或 $_SERVER['HTTP_AUTHORIZATION'] 也容易漏掉 Token 刷新、多守卫隔离等真实场景。必须把认证(who)和授权(what)拆开,用可插拔的链式中间件 + 显式上下文传递来兜住边界。
为什么不能在中间件构造函数里调用 Auth::check()
Webman 的中间件实例在应用启动时就初始化了,而 $request 是每次请求才注入的。你在 __construct() 里访问任何依赖请求上下文的东西(比如 $request->session()、$request->header('authorization')),都会得到空值或报错。更隐蔽的问题是:如果用了 Redis Session 驱动,但中间件没注册到容器里,Auth 类根本拿不到连接实例。
- 所有用户身份判断必须放在
handle()方法内,且优先从$request->getAttribute('user')取——前提是前置认证中间件已通过$request = $request->withAttribute('user', $user)注入 - 别写
if (!Auth::check()) { return $next($request); }这种放行逻辑,未认证用户应明确返回401,而不是“假装没这事”往下传 - 若用 JWT,解析失败时不要抛异常,而是统一返回
['code' => 401, 'msg' => 'token invalid'],避免框架层捕获异常后转成 500
CheckPermission 中间件怎么匹配路由所需权限
硬编码 if ($path === '/admin/user/delete') { $need = 'user:delete'; } 是反模式。Webman 路由不强制命名,$request->route() 在未命中时为 null,直接调 getName() 会触发 Fatal error。正确做法是路径前缀白名单 + 动态映射表。
- 配置免鉴权路径:
['/api/login', '/api/public', '/static/'],用str_starts_with($path, $prefix)快速跳过 - 对需鉴权路径,提取控制器动作:例如
/admin/user/edit/123→admin.user.edit,再查配置数组config('permission_map')['admin.user.edit'] ?? null - 避免用正则匹配路径——性能差且难维护;也不要用
explode('/', $path)[1]这种脆弱切分,URL 可能带 query string 或重复斜杠 - 权限标识统一小写+英文冒号分隔,如
post:publish、order:refund,和数据库permissions.name字段保持一致
权限数据从哪来?缓存失效时怎么不崩
每次请求都查 role_has_permissions 表,QPS 上 500 就拖垮 MySQL。但全靠 Redis 缓存又怕管理员刚改完权限,用户还卡在旧规则里。得有缓存 + 降级双保险。
- 用
Cache::remember("permission_{$roleId}", 3600, fn() => Db::name('role_has_permissions')->where('role_id', $roleId)->column('permission_id'))缓存角色权限 ID 列表,过期时间设 1 小时足够平衡时效与压力 - 缓存未命中时,**不要 fallback 到实时 DB 查询**,而是读本地配置
config/permissions.php返回默认规则(例如['super_admin' => ['*']]),防止 DB 挂了整个权限系统瘫痪 - 权限变更时,用事件监听器清除对应
permission_{$roleId}缓存,别用通配符del permission_*—— Webman 的 File 缓存驱动不支持通配删除 - 别用
Cache::get('xxx') === null判断缓存是否存在,File 驱动会把null当有效值存下来,改用Cache::has('xxx')
403 响应该返回页面还是 JSON?
Webman 默认不区分 AJAX 请求,$request->isAjax() 在某些代理或移动端可能不准。只看请求头 Accept 更稳妥,但要注意前端没设这个头时的兜底行为。
- 如果
$request->header('accept') === 'application/json'或包含json,返回json(['code' => 403, 'msg' => 'forbidden'])并设状态码 - 否则返回预渲染的 HTML 页面(如
view('error.403')),但禁止在中间件里调redirect()—— 它会触发 header 已发送错误,改用Response::create(...)->withStatus(403) - 别在中间件里 echo 或 print_r,Webman 的响应生命周期不允许中途输出
最易被忽略的一点:权限中间件必须和认证中间件严格串行注册,顺序错了,$request->getAttribute('user') 就是 null。而这个顺序错误,在本地开发时可能因 session 文件残留“看起来正常”,一上生产环境并发高了立刻暴露。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











