(Security)如何使用Voter检查用户权限(3.0)

在symfony中,你可以使用ACL模块来检测用户访问数据的权限,但是他过于复杂。你可以使用一个更简单的方式就是自定义voter,他就像简单的条件语句。

看看 authorization 这一章可以对 voter 有更深刻的理解。

Symfony如何使用voter

为了使用voter,你应该了解symfony与voter的互动机制。所有的voter都要使用isGranted()方法调用,并在symfony接受授权检查(这个security.authorization_checker服务)。voter来决定用户是否可以访问这些资源。

根本上,symfony从所有的voter中获取响应,并根据应用程序的策略做最后的决定(允许或拒绝访问这些资源),策略主要有:affirmative, consensus和unanimous.

更多信息请查看the section about access decision managers.

 

Voter Interface(接口)

自定义一个voter需要实现接口 VoterInterface或者继承Voter,他们可以让创建voter变得简单。

在symfony2.8新增了Voter这个辅助类。他和早期版本的AbstractVoter类类似。

 

设置:在控制器检查访问

假设你有一个post对象并且你需要决定当前用户是否可以编辑或是查看对象。在你的controller中,你需要检查访问,代码如下:

这个denyAccessUnlessGranted()方法(以及,简单的isGranted()方法)来唤起“voter”系统。现在,没有voter能够决定用户是否可以访问或者编辑一个Post。但是你可以使用自己的逻辑去创建你自己的voter来决定你想要的。

denyAccessUnlessGranted()函数和这个isGranted()函数都是快捷方式他们都调用security.authorization_checker服务的isGranted()

 

创建一个自定义的Voter

假设用普通的逻辑写法来决定用户是否可以访问或者编辑一个post对象是非常复杂的。例如,一个用户可以随时查看或者编辑他自己的post。或者这个post标记为公开,任何人都可以访问。但用一个voter就只需要这样做:

就是这样完成了!接下来,我们来配置他;

回顾一下,这里是上面的两个抽象方法:

Voter::supports($attribute, $subject)

当isGranted()(或者denyAccessUnlessGranted())被调用的时候,他的首个参数是$attribute(如 ROLE_USER,edit),第二个参数(如果有的话)为$subject(如 null,一个Post对象)。你的工作就是确定你的voter的vote(票)的attribute/subject组合。如果你返回true,voteOnAttribute()将会被调用。否则,你的voter完成后,一些其他的voter会继续这个过程。在本例中,如果attribue是viewedit并且object是一个Post实例,那么你将返回true。

voteOnAttribute($attribute, $subject, TokenInterface $token)

如果你从support()中返回true,这个方法就会被调用。你的工作很简单:返回true允许访问或者返回false拒绝访问。这个$token能够获取到当前的用户对象(如果有的话)。在本实例中,包含了所有复杂的业务逻辑,最终确定是否可以访问。

 

配置这个voter

去注入这个voter进入security,你一定要把他生成为一个服务并且标记他为security.voter:

完成了!现在,当你传入view/edit和一个Post对象给isGranted(),你的voter将被执行并能够控制访问了。

 

在voter中检查角色(Roles)

AccessDecisionManager是在2.8被引入的:在他之前使用会引起CircularReferenceException异常。在早期版本中,你一定要注入service_container,并且获取security.authorization_checker来使用isGranted().

如果你想从你的voter内调用isGranted() – 例如,你想看看当前用户是否有ROLE_SUPER_ADMIN角色。在这里你可以在你的voter中注入AccessDecisionManager。你可以这样使用它,例如,始终允许有ROLE_SUPER_ADMIN的用户访问。

下一步,编辑services.yml注入security.access.decision_manager服务

就是这样!调用AccessDecisionManager的decide()本质上和调用控制器或其他地方的isGranted()是一样的(他有点低级但是是voter所必须的)。

这个security.access.decision_manager是私有的。这意味着你不能直接从控制器访问:你只能将其注入到其他的服务中。那好吧:所有情况下都可以使用 security.authorization_checker除了voter。

 

更改这个Access Decision Strategy

通常情况下,在任何时间里只有一个voter将投票vote(其他的将“弃权”,意味着supports()将返回false)。但在理论上,你可以创建多个voters分别投票(vote)给一个action或者对象。比如说,你有一个 voter 用来检测一个站点的会员是否已经超过了 18 岁。

为了处理这样的情况,access decision manager将使用access decision strategy。你可以根据你的需求配置。这里有三种可用的策略:

affirmative (default)

一个voter授权访问时,给予授权。

consensus

当大多数的voter都允许访问时,给予授权。

unanimous

只有所有 voters 都允许授权的时候给予授权。

 

考虑上面的情况,也就是说我们所有的voter都允许访问才能允许授权,来读取post。在这种情况下,默认策略应该就不会在生效了,而是被unanimous所取代。你可以在安全配置里设置此参数:

 

发表评论