Symfony6+ApiPlatform2.8角色权限适配is_granted()问题咨询
基于Symfony 6 + API Platform 2.8实现角色-权限关联的权限校验方案
针对你的场景,**自定义安全投票器(Security Voter)**是最优解——这是Symfony官方推荐的扩展权限逻辑的方式,能完美适配你基于关联表的角色-权限体系,同时无缝配合API Platform保护路由。以下是具体实现步骤:
1. 统一权限标识定义
先把所有权限整理成常量,避免硬编码字符串,方便后续统一维护:
// src/Constant/PermissionConstants.php namespace App\Constant; class PermissionConstants { const USER_CREATE = 'user_create'; const USER_EDIT = 'user_edit'; const USER_VIEW = 'user_view'; const USER_DELETE = 'user_delete'; // 按需添加其他权限... }
2. 创建自定义安全投票器
执行Symfony命令生成投票器模板:
php bin/console make:voter PermissionVoter
然后修改投票器逻辑,核心是从用户关联的角色权限+自身额外权限中收集所有权限,再判断是否匹配目标权限:
// src/Security/Voter/PermissionVoter.php namespace App\Security\Voter; use App\Entity\User; use App\Constant\PermissionConstants; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; class PermissionVoter extends Voter { protected function supports(string $attribute, mixed $subject): bool { // 只处理我们定义的权限常量,subject可为null(全局路由权限)或实体实例(资源级权限) return in_array($attribute, [ PermissionConstants::USER_CREATE, PermissionConstants::USER_EDIT, PermissionConstants::USER_VIEW, PermissionConstants::USER_DELETE, // 补充其他权限... ]); } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool { $user = $token->getUser(); // 未登录用户直接拒绝 if (!$user instanceof User) { return false; } // 收集用户所有有效权限:角色继承的权限 + 自身额外权限 $userPermissions = []; // 提取角色关联的权限 foreach ($user->getRoles() as $role) { foreach ($role->getPermissions() as $permission) { $userPermissions[] = $permission->getCode(); } } // 追加用户自身的额外权限 foreach ($user->getUserPermissions() as $permission) { $userPermissions[] = $permission->getCode(); } // 去重后判断是否拥有目标权限 return in_array($attribute, array_unique($userPermissions)); } }
注意:确保你的实体类有对应关联方法:
- User实体:
getRoles()(返回关联的Role集合)、getUserPermissions()(返回关联的UserPermission集合)- Role实体:
getPermissions()(返回关联的Permission集合)- Permission实体:
getCode()(返回权限的字符串标识,如user_create)
3. 在API Platform中保护路由
方式1:通过实体注解配置
直接在API资源实体上用ApiPlatform注解绑定权限:
// src/Entity/User.php use ApiPlatform\Core\Annotation\ApiResource; use App\Constant\PermissionConstants; /** * @ApiResource( * collectionOperations={ * "get"={"security"="is_granted('" . PermissionConstants::USER_VIEW . "')"}, * "post"={"security"="is_granted('" . PermissionConstants::USER_CREATE . "')"} * }, * itemOperations={ * "get"={"security"="is_granted('" . PermissionConstants::USER_VIEW . "')"}, * "put"={"security"="is_granted('" . PermissionConstants::USER_EDIT . "')"}, * "delete"={"security"="is_granted('" . PermissionConstants::USER_DELETE . "')"} * } * ) */ class User { // 实体字段与关联逻辑... }
方式2:自定义控制器用Security注解
如果是自定义API控制器,直接用Symfony的IsGranted注解:
// src/Controller/CustomUserController.php use Symfony\Component\Security\Http\Attribute\IsGranted; use App\Constant\PermissionConstants; class CustomUserController { #[IsGranted(PermissionConstants::USER_CREATE)] public function createUser() { // 控制器业务逻辑... } }
4. 兼容Symfony原生is_granted()调用
在模板、服务或其他代码中,直接传入权限常量即可判断权限:
{% if is_granted(constant('App\\Constant\\PermissionConstants::USER_EDIT')) %} <button>编辑用户</button> {% endif %}
额外优化:封装权限收集逻辑
可以在User实体中添加方法封装权限收集逻辑,减少投票器代码冗余:
// src/Entity/User.php public function getAllPermissions(): array { $permissions = []; foreach ($this->getRoles() as $role) { foreach ($role->getPermissions() as $permission) { $permissions[] = $permission->getCode(); } } foreach ($this->getUserPermissions() as $permission) { $permissions[] = $permission->getCode(); } return array_unique($permissions); }
之后投票器中可直接替换为:
$userPermissions = $user->getAllPermissions();
实例级权限扩展(可选)
如果需要针对特定资源实例做权限校验(比如用户只能编辑自己的账号),可修改投票器逻辑:
protected function supports(string $attribute, mixed $subject): bool { // 新增支持针对User实例的编辑权限校验 return parent::supports($attribute, $subject) || ($attribute === PermissionConstants::USER_EDIT && $subject instanceof User); } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool { $user = $token->getUser(); if (!$user instanceof User) { return false; } // 实例级权限判断:管理员可编辑所有用户,普通用户只能编辑自己 if ($attribute === PermissionConstants::USER_EDIT && $subject instanceof User) { if (in_array(PermissionConstants::USER_EDIT_ALL, $user->getAllPermissions())) { return true; } return $user->getId() === $subject->getId(); } // 通用权限判断 return in_array($attribute, $user->getAllPermissions()); }
内容的提问来源于stack exchange,提问作者Elell
相关产品推荐
相关产品推荐

