深入 Symfony 框架用户身份验证

深入 Symfony 框架用户身份验证

Chris Yue No Comment
Posts

如果大家看过我之前的教程系列,应该都已经成功实现了用户注册/登录功能。但 Symfony 的防火墙内部到底都做了什么事情?或者说如果我们想自己创建一种新的防火墙,应该怎么做?

Symfony 框架是一个高组件化的框架,也导致 Symfony 的运用非常灵活。用户身份认证系统,就是一个典型。

其实任何一种技术,原理应该是比具体实现的代码要更重要的,所以本章我打算尽量少写代码,而是通过阐述原理的方式尽量让大家理解 Symfony 的身份验证技术。

看过教程的人,应该知道用户身份认证是由防火墙完成的。如果请求信息里没有包含用户身份信息,或者包含错误的身份信息。防火墙会直接拦截你的请求,并且根据配置,跳转到登陆页,或者返回要求提供的登录信息的 401 页面,或者其他任意的方式。

如果我们使用 Symfony 或者第三方库自带的防火墙类型,会感觉身份认证一瞬间就完成了。让我们把镜头放慢,深入到防火墙的运作中,我们会发现,防火墙也是由以下几块积木组成的:

  • Firewall listener
  • Token & Token Storage
  • Authentication provider
  • User provider
  • Entry pointer

Firewall Listener

作用:检查请求是否需要做身份验证。

防火墙监听器跟所有其他监听 kernel.request 事件的监听器没有本质上的区别,都会去处理 GetResponseEvent 事件对象。大家不用去搜索了,这个事件对象简而言之就是个容器,里面包含了 Request 对象,并且可以设置一个 Response 对象。如果 Response 对象被设置,无视后面所有事件,直接被框架返回 Response

Firewall Listener 到底具体做什么?首先它判断 Request 对象里是否包含身份认证信息,如果不包含,什么事都不做;如果包含,他会将 Request 里的用户登录信息抽取出来,创建一个 token。

例子:Symfony\Component\Security\Http\Firewall\BasicAuthenticationListener

Token & Token Storage

在 Symfony 里,token 是一个表示用户身份的容器,用来装用户的信息,并且它分为两种 token:一种是包含用户待验证身份信息的 token,就好像你去健身房如果办年卡,你需要先提供身份证;另一种是包含验证过的用户身份信息的 token,就好比健身房的柜子钥匙,拿到钥匙了也表示你可以进去健身了。

两种 token 可以是同一个类的对象,仅有的区别在于:待验证 token 里注入的是用户名;验证过的 token 注入的是用户对象。

Firewall listener 在创建好待验证 token 之后,用 AuthenticationManagerauthenticate 方法尝试验证 token。如果成功,authenticate 方法会返回验证过的 token。最后,将验证过的 token 放到 token storage 里面,就大功告成了。

例子:Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken

Authentication Provider

上面提到的 AuthenticationManager 实际上是将验证工作委托给了某个具体的,叫做 Authentication Provider 的服务来真正处理验证工作。大家可以把 Authentication provider 看成是健身房的前台。

验证过程:先从待验证 Token 里拿出用户信息,通过 username,利用注入的 User provider 的 loadUserByUsername 方法获取到一个用户,并通过比对获取到的用户信息以及 token 里的用户信息的(比如比对密码),来判断用户是否登录正常。如果正常,创建一个新的 token 并将用户对象注入后返回它。当然如果信息有问题,则会抛出 AuthenticationException 异常。

例子:Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider

User Provider

大家可以看成是健身房前台所用的电脑,通过用户提供的名字,可以从某个地方(不一定是数据库)查到此名字对应的用户的全部信息。当然比对工作还是由前台来做,而不是电脑,User provider 就是那个电脑,他只查用户,不验证用户。

例子:Symfony\Component\Security\Core\User\InMemoryUserProvider

Entry Point

定义了需要用户认证时,或者用户身份认证失败的时候的处理方式。Entry point 类的 start 方法也是处理 request 参数,返回 response,你可以在这里定义用户是直接显示 401 页面呢,还是跳转到登录页面(小提示:没有规定 401 页面是不可以显示登录表单的哦),还是只返回一个 WWW-Authenticate 的 HTTP 头。

例子:Symfony\Component\Security\Http\EntryPoint\BasicAuthenticationEntryPoint

总结

构成防火墙的四块积木,我已经全部介绍完了,不知道大家对 Symfony 的防火墙有没有更深的认识了呢?我这属于理论派的,大家可以实际操作一下。推荐官方教程:http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html,坏消息是全英文(对于英语不好的人来说),好消息是,只用看例子就足够了。

这里提醒大家,上面的教程会出现 A token is not found in security context 的错误,这是因为官方这篇教程并没有提到如何定义 Entry Point 导致 token storage 没有 token 时却没有 entry point 去处理它的情况,所以大家可以再参考这个 bundle,看看防火墙的完整定义应该是怎样的。

如果大家看到这里时头晕脑胀,感觉吃得太多不容易消化,可以看看后来 Symfony 新增的 Guard,其实就是普通用户认证系统的精简版。

最后,大家可看看我写的 dark-portal-bundle,里面基本运用了此文所有的知识点,希望能给大家一些启发。

深入 Symfony 框架用户身份验证 by Chris Yue is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

发表评论

fifty six − 50 =