Symfony Security Voters

嘿哥们,在程序中你可能遇到像这样的事情,“比如,你烤了一些饼干,但是不想让别人吃,只能你自己享用”,也就是说你希望能够知道当前用户是否能编辑、删除或者查看自己的东西。我们就可以充分利用symfony的特殊功能:security voters。

今天的应用:DeliciousCookie

我们使用一个页面展示所有的饼干,并使用用户名ryan和密码为cookie的用户登录,并且每一个饼干都有一个“nom”按钮,我点击它就等于我吃掉了他,他会从数据库中删除。真是高科技,哈哈。

这个应用程序非常的简单:我们有一个AppBundle当然里面还有一个DeliciousCookie实体。这个DeliciousCookie里有一个bakerUsername属性,他来存储究竟是谁制作的这个饼干。为了让这个程序尽量简单,我们没有使用User实体,而是在security创建一个username字符串。

现在的程序,每个人都可以吃这个饼干,不管谁做的。但是我们的目标是让你吃到你做的饼干。该CookieController里面会输出饼干列表页面,并谁在里面点击,就删除数据库里的信息,并设置提示信息。

唯一有趣的是security.yml。我们有两个硬编码用户:ryan和leanna,并且access_control会让登录后的用户查看/cookies页面.

禁止访问:简单方法

防止我吃别人的饼干是很简单的。首先,要做的就是在controller中写逻辑。所以你在这里这样做就行:如果你现在登录的名字不等于饼干作者名字,就会抛出一个AccessDeniedException提示“你不能吃它”。

现在,如果你偷吃别人的饼干就会被抓。在生产环境下,只会提示一个403错误页面。

我们还需要在twig中写一些逻辑

你可以看到,我们隐藏了不属于我的NOM按钮。但是在安全写安全逻辑时,特别是保护饼干的逻辑时,我们不想它在你的程序中重复使用,因为如果你改变一些东西,你很可能忘记更新这一部分,你可能会出大问题。

///////////////////////////

创建一个Voter

因此,Voter的目标是让我们集中在逻辑,所以我们只有在一个地方写这些。你需要创建一个Security目录里面有一个CookieVoter。我们将使用symfony2.6配备的一个梦幻般的类AbstractVoter。如果你使用symfony2.5或者更低,你其实可以从互联网上找到这个类,只不过现在他内置了。现在加入声明并extends CookieVoter。

PHP告诉我,我需要填写三个抽象方法,来完成非常棒的功能。

什么是Voter他能做什么?

因此,让我们回头看看,因为我并没有告诉你什么是这些Voter要做的。首先当我们完成这些代码时,我希望他们看起来很规整。而不是混乱的手工逻辑,这需要我们修改之前的代码,使用isGranted函数,传入NOM参数他很重要他会让我明白应该做什么,然后,传入$cookie对象作为第二个参数:

这个isGranted在symfony2.6被引入,他所做的就是不用使用security.context服务,直接调用isGranted就可以。因此,要根据你的symfony版本来判断,是否手动使用security.context服务。

在幕后,当你使用isGranted功能,symfony会召唤出一堆的Voter,并要求他们每一个人告诉我们是否应该访问。例如,当你传入ROLE_SOMETHING到isGranted就像ROLE_USER,有一个RoleVoter类它试图找出当前用户是否能作用到你访问的有关类。

然而,这些人并不知道,你可以发明这些字符串,作为这些人的操作标签。因此,在这种情况下,我发明了NOM,并且我们要添加一个新的Voter到系统,就是跟symfony说:“嘿symfony,每当这个NOM属性传入isGranted就告诉我”,为了让他工作,我们需要填写getSupportedClasses和getSupportedAttributes功能

填写CookieVoter

首先,在getSupportedClasses,他要返回DeliciousCookie类字符串;

他告诉symfony,我们传入DeliciousCookie对象到isGranted。我们会在getSupportedAttributes里做同样的事情,让他返回一个数组里面有NOM字符串。

他告诉symfony,我们把NOM传入isGranted。每当上面的两个函数成立,isGranted这个底层函数将被调用。

现在我们使用var_dump准备打印一下,属性对象和用户;

注册和标注你的Voter

我们的Voter类已经蓄势待发。但是symfony还不能自动找到他。要告诉symfony我们的新Voter要注册成一个服务,并给他一个特殊标签;

我们有一个app/config/services.yml文件他会被添加到config.yml文件,我们要在这个文件中添加服务:

这个名字并不重要,尽量保持短小。

重要的是tags部分,你需要添加tag名字security.voter。

让我们试试吧!当我们刷新,我们的看到有东西输出,证明是我们的Voter被调用。

 

保护您的饼干逻辑

现在事情开始变的酷了。因为我们理论上有ACCESS_CONTROL,这个Voter不应该能够进来,除非用户已经登录,为了保险起见我们还是使用is_object检查一下,看看是否有用户记录:

请记住,这样做是因为在symfony2如果你是匿名用户他会是一个字符串。从这里开始就是纯逻辑:如果Baker’s等于当前用户名,我们就让他进入。否则我们就让他滚蛋:

因此,让我们刷新“NOM”请求,他工作了!我们登录Ryan,需要点击按钮才能知道Ryan的饼干才能吃,体验不好。现在我们进入twig模板,使用is_granted函数,做同样的事情。

现在刷新,你看到了你期望的效果。

 

给ROLE_COOKIE_MONSTER角色的用户特殊访问

现在我们让事情变的困难一些。在security.yml中给leanna一个特殊的用户角色ROLE_COOKIE_MONSTER:

如果你有这个角色,你可以吃任何人的饼干,即使你一个饼干都没有做。似乎你是一个混蛋,当我们看看尝试一下。

要做到这一点,我们基本上要告诉isGranted从安全中获取角色。现在,我们无法获得安全的信息,所以我们要使用依赖注入。我们要注入security.context。唯一的问题是,因为我们的内部体系,如果我们试图注入安全系统到这里会得到一个循环的依赖。相反,我们可以注入整个容器,是的这样不怎么好,但是这种情况下,没有办法,当然也不会给我们带来什么伤害:

回到services.yml添加一个参数键,使用依赖把@service_container(容器)注入到__construct函数。

下一步赶快去isGranted写逻辑:

现在我们使用symfony2.6的全新服务security.authorization_checker。如果使用旧版本,就要用security.context,当然你不必担心,因为symfony2.6仍然存在,直至symfony3.0才会消失。这样我们就可以使用相同的isGranted功能,检测用户角色为ROLE_COOKIE_MONSTER的用户是否能够访问。

我们尝试一下区别,现在我们登录ryan,没有什么变化。我们退出,登录leanna,真的可以随便吃。

 

添加多个操作(NOM,DONATE[捐赠])到一个Voter

我还想要一些疯狂的事情发生。让我们捐出我们的饼干给朋友。现在我知道这很疯狂,为什么我做的饼干要捐给别人?我们就是尝试一下。实际上没有这样的逻辑代码,但是没关系。我们进入index.html.twig添加一个链接,我们只是去体验一下,正常的隐藏和显示是否正确。

就像之前一样我又发明了DONATE字符串。如果我们不做别的,只是刷新,你会发现链接显示不出来。如果没有这样的属性,在我们的Voter中就会返回false。我们要使用getSupportedAttributes功能。

让我们更新他加入一个DONATE

现在isGranted可以处理两个不同的属性NOM和DONATE了。我们更新逻辑:

在我们的例子中,因为巧克力饼干最好吃,所以我们就放弃不带巧克力的饼干。所以我们要写这些逻辑,要是出现了巧克力这个词我们就,不送人了,但是如果没有,你可以把他捐赠

在这个函数底部,我们还存在一个return false。他在技术上没有道理。如果我们在isGranted中传入NOMDONATE以外的参数,symfony也不会调用我们的Voter因为这个getSupportedAttributes。

所以在这里你可以放任何东西,我喜欢抛出一个异常。但是你会没事的:

酷,让我们来看看饼干,你可以赠予了。这一次我们看到了捐赠链接不属于巧克力饼干。那就完美了。

让我们用一些常量

我们有NOM和DONATE赤裸的字符串在程序中,他不是很好的方式,我们应该用一个常量代替。以为我们创建两个常量ATTRIBUTE_NOMATTRIBUTE_DONATE:

然后,我们把他使用在getSupportedAttributes和isGranted中。

我们的CookieController也可以使用这个常量:

是的,你也可以使用twig的constant()函数来在模板中调用这些常量,对于我来说放到twig中有点丑,所以我就写到这里

去吧Security Voters!

当你需要弄清楚,如果用户访问一些特殊的对象,是否可以,你就需要security voters来解决。她有着我喜欢的功能之一,她可以很简单的制作模板逻辑和控制器逻辑。

symfony也有ACL系统,但是它令人难以置信的复杂,如果你有非常复杂的授权要求,我才建议你使用它。如果你只想用几行代码,就弄清楚,用户是否能访问这些,在Voter就可以,不必使用ACL。

 

 

 

发表评论