在symfony3.0中使用Guard很容易的完成身份验证

symfony2安全系统是框架比较复杂的地方,很多人很难明白并运用到工作中。他非常的强大和灵活,但老实说他并不简单。对于自定义身份验证,symfony之前版本有一些文章。

现在symfony已经更新到了3.0,一个新的组件也被包含到了symfony框架体系:Guard。这个组件的目的是与安全系统集成,并提供一个更简单的方法。他公开了一个单个接口,把你从身份验证开始到结束都链起来:逻辑和所有组合都在一起。

在这篇文章里,我们来创建一个简单的表单验证,需要一个用户登录并给他ROLE_ADMIN角色到每一个页面。原始的方式是创建一个form authentication,他现在仍然可以使用,但我们将使用这样一个简单的步骤来举例说明Guard。你就能够了解这个概念并复用到其他的验证(token,社交媒体等)。

安全配置

这个安全配置将需要一个用户类(来表示用户数据)还需要UserProvider(获取用户数据)。为了让事情简单,我们直接使用InMemory用户提供器(user provider)自然而然的我们使用默认的symfony的User类。我们的security.yml就是这个样子:

更多关于symfony安全系统的文章请阅读book。

我们的InMemory提供器现在有一个写死的test用户并付给他ROLE_ADMIN.

下面是我们定义的防火墙:

上面基本上是说,匿名用户可以访问防火墙路径,还记录了用户退出路径。这个新的guard键是表明防火墙使用Guard配置的验证器:form_authenticator。他传入的是服务名称并且我们将在下一分钟内看到他的定义。

最后在安全配置中,我们可以指定一些访问规则:

在这个示例中,我们指定没有登录的用户只能访问/login路径。对于其他用户要有ROLE_ADMIN角色。

 

登录控制器(Login Controller)

在真正做身份验证之前,让我们看看实际的登录表单和控制器。DefaultController的action里面:

定义了/login路由,这个action只是负责显示一个基本的登录表单,用户不登录。twig模板如下:

到目前为止,没啥特别的。只是一个简单的表单html。

 

Guard Authenticator 服务

我们在安全配置文件中引用了我们的Guard authenticator服务。让我们确保在sservices.yml中有这个服务定义:

这个服务引用了FormAuthenticator类:

尽管他看起来似乎很多,其实不然。让我们一步一步去了解发生了什么事情。

首先,这个文件就在我们bundle的Security文件夹下,这个是一个个人习惯的选择,你随意。然后,我们继承AbstractGuardAuthenticator,因为他已经实现了GuardAuthenticatorInterface接口所有必须要实现接口的方法。如果我们需要一个特定的token类来完成我们的验证,我们仅仅需要实现这个接口极其createAuthenticatedToken方法。现在我们不需要。

实现这个接口的方法是问题的关键,验证都在这里,这个包括当一个用户访问到授权或者拒绝访问所有流程。

我们从这个getCredentials()方法开始,得到每个请求。这个方法的目的是从请求获取凭证数据并返回,或者返回null(会拒绝访问,或者允许其他验证器提供凭证,或者调用start()方法)。如果是这个案例:凭证是通过/login的提交post数据,我们会获得到提交的用户名和密码,并返回一个数组。

如果getCredentials()方法没有返回空,则顺利进入下一个方法getUser().getUser()负责加载用户,主要根据前面getCredentials()方法获得的凭证来获得用户。使用默认的用户提供器(我们案例里的InMemory提供器),基于用户名返回要加载的用户。我们也可以在这里返回null,他会触发验证失败,我们也可以选择CustomUserMessageAuthenticationException去指定我们自定义的失败消息。

如果上面用户被获取到并返回,这个checkCredentials方法就开始生效。这个方法的目的是核查传入的凭证和找到的用户是否匹配。更上面一样的道理,我们返回null或者抛出一个异常,验证失败。

此时,如果凭证匹配用户就可以登陆了。在这种情况下,onAuthenticationSuccess()方法被调用,他能做我们想做的事情。例如我们的案例,重定向到主页上就死一个很好的主意。相反的,如果验证失败,onAuthenticationFailure()方法就会被调用。我们需要做的就是重定向到/login页面,我们会把最后验证的错误信息放在session中,在表单模板显示出来。

start方法是Guard系统(或者应用)的切入点。这个方法是当用户尝试访问一个需要验证的页面时,他没有通过getCredentials()方法返回有效凭证,就会调用这个方法。在我们案例中就是如果有人试图访问主页,getCredentials()获取不到有用的请求,返回null,没有凭证。我们就将他重定向到/login页面,让他登录后访问主页。

让我们想象一下其他例子:基于token的验证。在这种情况下,每个请求需要包含一个token,来验证用户。这意味着getCredentials()将总能返回凭证。如果没有,start()方法将返回一个响应,拒绝访问。

最后,一个方法是负责标记remember-me功能的。在我们的例子中,没有使用它,所以返回false。关于Remember-Me的更多信息请查看symfony cookbook。

 

总结

我们现在使用Guard组件创建了一个全功能的登录系统。还有我们上面刚刚提到的token验证的例子,他与我们今天文章主要介绍的一起工作。可以存在多个身份验证器:

如果我们配置多个验证器,我们需要去指定哪一个是entry point(当一个用户尝试访问一个资源但是没有提供凭证,start()方法将会被调用).

也就是说,我们创建多个验证器,我们提交的数据符合其中的一个即可通过验证,如果都不符合,会调用entry_point指定验证器的start()方法。

注意:Guard并不替换任何的symfony验证,只是补充。现有的将会继续工作。例如,form_login或者simple_form,我们之前使用过的,他们都将继续工作。

发表评论