symfony权限控制依赖voter+accessdecisionmanager+策略协同,默认affirmative策略(一票通过),支持consensus、unanimous;voter以php类实现权限逻辑,需正确注册并配合is_granted()调用。

Symfony 的访问决策机制不是靠“开关式配置”就能生效的,它依赖投票器(Voter)+ 决策管理器(AccessDecisionManager)+ 策略(Strategy)三者协同工作。核心不在“配什么”,而在“怎么让投票结果被正确汇总并执行”。
决策策略决定投票如何被采纳
Symfony 默认使用 affirmative(一票通过)策略:只要有一个 Voter 返回 ACCESS_GRANTED,就允许访问。你也可以显式配置其他策略:
- affirmative:一个赞成即通过(默认)
- consensus:赞成票多于反对票才通过
- unanimous:所有 Voter 都必须赞成才通过
在 config/packages/security.yaml 中设置:
access_decision_manager:
strategy: 'consensus'
投票器是权限逻辑的实际执行者
Voter 负责判断某用户对某个资源(如 Document、Post)是否有特定权限(如 VIEW、EDIT)。它不直接写在 YAML 里,而是以 PHP 类形式实现:
- 继承
AbstractVoter或实现VoterInterface - 重写
supports()判断是否处理当前 attribute / subject - 重写
voter()执行具体业务逻辑(比如检查文档所属人、部门权限等)
示例片段(DocumentVoter):
if ($attribute === 'EDIT' && $subject instanceof Document) {if ($token->getUser() === $subject->getOwner()) {
return self::ACCESS_GRANTED;
}
}
决策管理器协调所有投票器
默认的 AccessDecisionManager 会自动收集所有已注册的 Voter 并调用它们。你一般不需要自定义它——除非有特殊聚合逻辑(如加权投票、超时熔断等)。
- 自定义需实现
AccessDecisionManagerInterface - 必须在服务定义中标记为
public: false,并在security.yaml中显式指定服务名 - 注意:手动注册 Voter 到自定义 Manager 时,需确保
$voters数组被正确注入,否则投票不生效(这是常见报错根源)
权限检查要配合正确的调用方式
仅配置 Voter 和策略还不够,代码中得用对方法:
- 控制器内:
$this->isGranted('EDIT', $document)—— 自动触发所有 Voter 投票 - 模板中:
{% if is_granted('DELETE', post) %} - 路由级控制仍走
access_control,但它只认角色(ROLE_*)或内置表达式(如is_authenticated()),不走 Voter
想在 access_control 中用 Voter 逻辑?得改用 表达式语法,例如:{ path: ^/doc/, roles: "is_granted('VIEW', request.attributes.get('document'))" }











