权限验证不能硬编码if-else,因其导致逻辑分散、维护困难;策略模式将权限判断抽离为可替换、可测试、可配置的独立单元,通过统一接口和依赖注入实现灵活切换与组合。

权限验证为什么不能硬编码 if-else
硬写 if ($role === 'admin') { ... } 或嵌套多层判断,会导致权限逻辑散落在各处,加个“编辑草稿”权限就得翻 5 个文件改条件。一旦角色体系变(比如新增 'reviewer' 或支持按部门动态授权),整片代码就成债务。
策略模式在这里的核心价值不是“炫技”,而是把「谁有权限」这个判断,从流程里剥出来,变成可替换、可测试、可配置的独立单元。
定义统一的权限策略接口
所有具体权限检查逻辑必须实现同一个契约,否则无法动态切换。接口只需一个方法,参数清晰:当前用户上下文 + 请求资源标识。
示例接口定义:
interface PermissionStrategy
{
public function allows(object $user, string $resource, string $action): bool;
}
注意:$user 是对象而非字符串或 ID,方便策略内调用 $user->getRoles()、$user->getDepartmentId() 等扩展属性;$resource 推荐用控制器/路由名(如 'post'、'api/v1/orders'),别用数据库表名。
实现不同策略并注入到验证器
常见策略包括角色基(RBAC)、属性基(ABAC)、白名单兜底等。关键不是堆数量,而是让它们能共存、可组合:
-
RoleBasedStrategy:查$user->roles数组是否包含预设角色,不查数据库——性能敏感路径必须内存态 -
AttributeBasedStrategy:比如判断$user->departmentId === $resource->departmentId,适合部门隔离场景 -
ConfigWhitelistStrategy:从配置文件读白名单(如['dev' => ['*']]),用于本地调试绕过
验证器本身不 new 具体策略,而是接收策略实例(构造函数注入或 setter 注入):
class PermissionGuard
{
private PermissionStrategy $strategy;
public function __construct(PermissionStrategy $strategy)
{
$this->strategy = $strategy;
}
public function authorize(object $user, string $resource, string $action): bool
{
return $this->strategy->allows($user, $resource, $action);
}
}
运行时切换策略的坑与解法
真实业务中,不是所有接口都走同一套规则。比如后台管理页用 RBAC,API 接口可能要结合 JWT claim 做 ABAC,而某个导出功能需额外检查用户当日操作次数——这时别强行塞进一个策略,用组合模式更稳:
- 写一个
CompositeStrategy,内部持有一组策略,按顺序执行,任一返回true即放行(OR 逻辑),或全部返回true才放行(AND 逻辑) - 避免在策略里做重定向或抛异常,那属于控制器职责;策略只返回
bool,由 guard 统一处理拒绝响应 - 切忌在策略中调用
$_SESSION或全局函数,这会让单元测试无法 mock 用户上下文
最易被忽略的一点:策略类自身不该持有 Doctrine EntityManager 或 Laravel 的 Auth::user(),所有依赖必须显式传入。否则你永远不知道它在 CLI 命令、队列任务、API 请求里行为是否一致。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











