Symfony 2.8升5.4:无ROLE_前缀角色is_granted权限拒绝问题
问题分析与解决方案
你的核心问题是:Symfony 5.4的默认RoleVoter只识别ROLE_前缀的权限属性,而你的自定义CustomVoter因为逻辑限制,无法处理MODULE_开头的权限检查,导致is_granted('MODULE_TRAINING')返回拒绝。
自定义Voter的问题点
- 构造函数硬编码了
$this->prefix = 'ROLE_',忽略了传入参数,且后续逻辑只处理该前缀的属性 supportsAttribute方法仅支持ROLE_开头的属性,导致MODULE_TRAINING这类属性不会被此Voter处理- 默认
RoleVoter同样只处理ROLE_前缀,所以MODULE_*类型的权限检查没有对应Voter响应
修复方案:调整自定义Voter逻辑
修改CustomVoter,让它支持匹配任意字符串类型的权限属性(直接对比用户角色列表):
namespace WORD\CoreBundle\Voter; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\CacheableVoterInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; class CustomVoter implements CacheableVoterInterface { /** * {@inheritdoc} */ public function vote(TokenInterface $token, $subject, array $attributes) { $result = VoterInterface::ACCESS_ABSTAIN; $userRoles = $token->getRoleNames(); foreach ($attributes as $attribute) { if (!\is_string($attribute)) { continue; } // 处理ROLE_PREVIOUS_ADMIN的弃用提示 if ('ROLE_PREVIOUS_ADMIN' === $attribute) { trigger_deprecation('symfony/security-core', '5.1', 'The ROLE_PREVIOUS_ADMIN role is deprecated and will be removed in version 6.0, use the IS_IMPERSONATOR attribute instead.'); } $result = VoterInterface::ACCESS_DENIED; // 直接检查用户是否拥有该属性对应的角色 if (in_array($attribute, $userRoles, true)) { return VoterInterface::ACCESS_GRANTED; } } return $result; } public function supportsAttribute(string $attribute): bool { // 支持所有字符串类型的权限属性 return true; } public function supportsType(string $subjectType): bool { // 支持所有类型的subject(REST控制器无subject,不影响) return true; } }
后续配置步骤
- 注册Voter:在
config/services.yaml中添加Voter的服务标签:
services: WORD\CoreBundle\Voter\CustomVoter: tags: - { name: security.voter } # 可选:设置优先级高于默认RoleVoter,确保优先触发 # priority: 200
- 禁用默认RoleVoter(可选):如果不想保留默认RoleVoter的逻辑,在
config/packages/security.yaml中禁用它:
security: voters: - { name: Symfony\Component\Security\Core\Authorization\Voter\RoleVoter, enabled: false }
- 验证角色加载:在控制器中临时添加调试代码,确认Token中的角色列表正确:
public function testAction() { $token = $this->get('security.token_storage')->getToken(); var_dump($token->getRoleNames()); // 输出内容应包含MODULE_TRAINING }
内容的提问来源于stack exchange,提问作者oracle972
相关产品推荐
相关产品推荐

