当我想向具有不同角色的用户授予访问权限时,我遇到了问题。 当我在表上下文中存储一些角色时,我为此创建了一个自定义投票者。
<?php
namespace App\Security\Voter;
use App\Constants\UserRoles;
use App\Entity\Team;
use App\Entity\User;
use App\Repository\UserTeamRepository;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class TeamVoter extends Voter
{
public function __construct(
private UserTeamRepository $userTeamRepository,
)
{}
/**
* @inheritDoc
*/
protected function supports(string $attribute, mixed $subject): bool
{
if (!in_array($attribute, UserRoles::TEAM_ROLES)) {
return false;
}
return $subject instanceof Team;
}
/**
* @inheritDoc
*/
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
$team = $subject;
if (in_array($attribute, UserRoles::TEAM_RELATED_ROLES)) {
return $this->voteOnTeamRelatedAttribute($attribute, $user, $team);
}
return match($attribute) {
UserRoles::ROLE_TEAM_ADMIN => $this->canEdit($user, $team),
UserRoles::ROLE_TEAM_OWNER => $this->canDelete($user, $team),
default => false,
};
}
private function voteOnTeamRelatedAttribute(string $attribute, User $user, Team $team): bool
{
$userRoles = $this->userTeamRepository->getUserTeamRoles($user, $team->getExternalUuid());
if (!$userRoles) {
return false;
}
return match($attribute) {
UserRoles::ROLE_TEAM_MEMBER_ADMIN => in_array(UserRoles::ROLE_TEAM_MEMBER_ADMIN, $userRoles),
UserRoles::ROLE_TEAM_MEMBER => in_array(UserRoles::ROLE_TEAM_MEMBER, $userRoles),
default => false,
};
}
private function canEdit(User $user, Team $team): bool
{
if ($this->canDelete($user, $team)) {
return true;
}
return in_array(UserRoles::ROLE_TEAM_ADMIN, $user->getRoles());
}
public function canDelete(User $user, Team $team): bool
{
return $team->getOwner() === $user || $team->getCreator() === $user;
}
}
现在我希望具有角色 ROLE_TEAM_ADMIN 的用户或具有角色 ROLE_TEAM_MEMBER_ADMIN 的用户可以访问该方法(注意: ROLE_TEAM_ADMIN 更多的是编辑所有团队的全局角色,而 ROLE_TEAM_MEMBER_ADMIN 更专注于单个团队,因此自定义投票者检查mm表的内容)
这是我的控制器中受保护的方法:
#[Route('/rest/team/{externalUuid}/member/add', name: 'add_team_member', methods: ["PUT"])]
#[IsGranted(UserRoles::ROLE_TEAM_ADMIN, subject: 'team'), IsGranted(UserRoles::ROLE_TEAM_MEMBER_ADMIN, subject: 'team')]
public function addTeamMember(Request $request, Team $team): JsonResponse
{
$request = $this->transformJsonBody($request);
...
文档表示可以配置授予决策策略,但其默认值设置为“肯定”,这意味着如果参与授予的其中一位投票者表示可以,则授予访问权限。
我的问题是,这并没有按预期工作,而是遵循“一致”决策模式。我已经浏览过 stackoverflow 上的这个主题,有些人建议使用表达式,但这些答案已经过时了,因为它适用于 symfony 5 和已弃用的 Sensio 安全包。
调试后,我注意到 Symfony\Component\Security\Core\Authorization\AccessDecisionManager 类收集结果(在collectResults方法中),查找投票者并在此基础上构建其决策,但属性数组始终带有一个属性只是。
所以我猜我的注释是以错误的方式定义的,但我在文档中找不到任何有关如何处理决策管理器的相关示例,显然设置一个又一个注释是不够的。
有人有好的方法来处理这个问题吗?
我相信使用多个
IsGranted()
调用与使用表达式 $this->isGranted() && $this->isGranted()
相同。
要使用
OR
逻辑运算符而不是 AND
,请查看 表达式语法。您可以在基础 #[IsGranted(string)]
的能力之上进行非常复杂的检查:
use Symfony\Component\ExpressionLanguage\Expression;
// ...
#[Route('/rest/team/{externalUuid}/member/add', name: 'add_team_member', methods: ["PUT"])]
#[IsGranted(
new Expression(
'is_granted("' . UserRoles::ROLE_TEAM_ADMIN . '", subject) or ' .
'is_granted("' . UserRoles::ROLE_TEAM_MEMBER_ADMIN . '", subject)'
),
subject: 'team'
]
public function addTeamMember(Request $request, Team $team): JsonResponse
{
$request = $this->transformJsonBody($request);
// ...
}