Symfony 框架实战教程——第四天:用 HWIOAuthBundle 实现第三方登录

Symfony 框架实战教程——第四天:用 HWIOAuthBundle 实现第三方登录

Chris Yue 13 comments
Posts

昨天我们添加了新闻相关功能,今天我们来实现用户功能,毕竟我们不可以随便让用户来我们这里发布新闻。

为了方便用户能快速发布内容,我们只用要求 QQ 的第三方登录就行了。

如果你打算跟着我一起完成这个项目,可能会因为 QQ 需要验证你是否有个人域名而卡在这一章,我写了一篇不用 OAuth 的。但本篇文章也最好看看,大部分知识点是完全一样的。

使用 HWIOAuthBundle 实现第三方登录

可以想象得到,第三方登录这种常见需求,相关的库也是有的。这里我再推荐一个:HWIOAuthBundle

如同昨日,我们先将它通过 Composer 引入到我们项目当中:

并将其注册到 AppKernel 里:

用过 OAuth 的同学应该知道,一个完整的 OAuth 客户端程序会包含许多链接。HWIOAuthBundle 已经有一部分写好的路由,我们将它们引入到项目当中:

Bundle 里可以自带路由规则以及对应的控制器,这也是 Bundle 的特点之一。

然后依照 HWIOAuthBundle 的文档,我们需要做一些配置:

此配置定义了需要使用 HWIOAuthBundle 登录后才能访问的地址。但是我们可以看到配置里并没有地址,只有一个防火墙名称,这是怎么一回事?先不用管我们接着添加配置:

app/config/security.yml 是专门用来配置用户访问权限的文件。其中 firewalls 里定义了若干防火墙。可以看到,第一个防火墙就是刚才配置的防火墙名称。

secured_area 防火墙里,可以定义此防火墙作用的路径。由于开启了 HWIOAuthBundle 的缘故,防火墙可以支持 oauth 配置,里面定义了登录路径 login_path,登录失败去的路径 failure_path,他们的值都是 HWIOAuthBundle 用来登录的路由名。这里教大家一个实用的命令,可以查看当前项目里都已经定义了哪些路由:

最后还有一个 oauth_user_provider 选项,目前我们暂时不用管它。

如果您是按步骤看到这里来的,你会发现在 firewalls 里还定义了其他的防火墙 devdemo_logindemo_secure_area。因为之前被删掉的 AcmeDemoBundle 自带了权限控制和登录表单的演示,所以定义了两个 demo_*** 的防火墙,目前已经可以放心删除。最后还剩一个 dev 防火墙,可以看到它定义了静态文件目录 css|images|js 可以不需要通过防火墙就能访问,另外 _(profiler|wdt) 这两个目录包含了开放工具的访问路径,也没有必要受防火墙的保护。

这里还需要注意防火墙定义的顺序和有限级:配置位置越靠前,优先级越高,比如说当用户访问一个路径 /css/main.css 的时候,第一个防火墙配置(虽然此配置其实是用来关闭防火墙的……)已经生效,就不会再往下匹配了。

HWIOAuthBundle 已经帮我们做了很多代码上的工作,比如 QQ 的 OAuth 登录地址是什么,参数名是什么……这些 HWIOAuthBundle 都已经帮我们设置好了,我们需要做的仅仅是定义一个 OAuth 服务端,告诉 HWIOAuthBundle 帐号是谁提供的(这里有一个 HWIOAuthBundle 目前支持的 OAuth 服务端列表),以及设置 api_key 和 api_secret:

定义好 OAuth 服务端后,我们再在 secured_area 防火墙里将 OAuth 服务端的回调地址设置上:

注意这里的 resource_owners 其实指的是 resource_owner callback path,作者完全可以取一个更准确的名字。

目前我们还没有定义名为 check_qq 的路由,赶紧加上:

我们可以发现,这里并没有为某个路径对应一个控制器,这又是怎么一回事?我只先透露通过事件监听器是可以做到的,目前就不多说了。

让我们再回到 security.yml 文件。这里再简单说说 firewall 的设计思想。每一个 firewall 都会定义了以下一些事情:

  1. 是开启还是关闭 firewall,如果 security: false,就是指此 firewall 是被关闭的,比如 dev 防火墙
  2. 哪些路径适用于此 firewall 规则,这通过 pattern 来指定,可以参考 dev 防火墙的配置
  3. 如果用户没有登录,将以什么方式登录,比如常见的使用登录表单的方式,如果使用此种方式,还要定义将引导用户去什么路径登录,以及那个路径是用来检查用户登录信息输入的。这里需要注意的是,登录页面的路径是不可以被防火墙保护起来的(不过有一种情况除外,后面再说),原因我就不说了吧,还是很容易想到的。

其他还有很多控制行为细节和技术细节的东西,这里就不多说了,遇到的时候自然会说的。

在用户没有登录的时候,开发工具栏也会有显示,方便大家了解当前的登录状态,未登录的时候将会如下显示

screenshot symfony 开发工具显示登录状态

在使用防火墙的路径访问时,Symfony 检查访问者一个叫做 Auth Token 的东西,就好象武侠片里的太监进皇宫,手里得有令牌证明自己的身份一样。要想取得令牌,就得去登录页面登录获取。防火墙的工作也及其简单:带着令牌的,欢迎!没有令牌?出去!只不过,有一种情况除外,那就是防火墙可以设置未登录的访问者,各个都自动颁发一个叫 anonymous auth token 的东西,匿名访问。此时虽然用户并没有真正登录,但是却跟登录的状态一样。如果是这样一种配置,等于防火墙是都放行的,所以还需要一个叫 access_control 的东西,来检查每一个用户拿的都是什么样的令牌,可以做什么样的事情。

知道了以上概念,可以说,做身份检查其实可以有两种风格,第一种风格,不登录没有令牌,配置好防火墙的访问路径,将不能公开访问的“皇宫”用墙直接隔开起来;第二种风格,大家都可以进,通过权限控制的方式来限制“某些身份不明不白的人”的行为。

我个人是比较喜欢第一种方式。我喜欢让防火墙检查用户“你是谁”,而让访问控制器检查“你能做什么”这种明确的分工方式,而且,本来就是公开的区域还需要给用户创建“令牌”对象我觉得是一个比较浪费的做法。不过,因为 HWIOAuthBundle 在登录界面的时候,必须要检查 Auth Token(按理来说应该先检查有没有 Auth Token,再检查 Auth Token 是什么身份),这里我们只能采用第二种方式。

我们将让 secured_area 可以匿名访问(还叫 secured 这样好吗……)

然后在 access_control 里设置,在创建新闻以及修改新闻的时候,要求必须登录:

到此,我们权限相关的配置已经完成了。

不过,事情还是没结束……在 Symfony 的世界里,每一个在网站访问的登录用户,都需要一个叫做 User Provider 的东西来生成,在 Symfony 里 UserProvider 都需要实现 UserProviderInterface,我们只需要实现此接口,返回一个 Symfony 的用户对象就行。不过还好 HWIOAuthBundle 已经帮我们实现了这个接口,并且已经为其定义好了服务,我们只用使用它就可以了:

事情到这个地步,还没有结束……好吧,其实已经结束了。现在我们可以访问一下 /news 路径,看看是不是会自动跳转到 /login 页面,是否会从 QQ 网站登录并成功返回登录的用户名:

screenshot symfony 开发工具显示登录成功

可以看到,虽然 HWIOAuthBundle 有这样那样的问题,但是不得不承认,它还是极大的提高了开发的效率。

最后记得将测试用的 in_memory 类型的 user provider 删掉。

里世界的第四天

扩展阅读:深入 Symfony 框架用户身份验证

Symfony 框架实战教程——第四天:用 HWIOAuthBundle 实现第三方登录 by Chris Yue is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

13 Comments

dmd

八月 6, 2018 在 2:34 下午

我想覆写这个HWIOAuthBundle

刘浩

二月 23, 2018 在 11:32 上午

你好

我按照您的步骤然后,出现这个这个错误
There is no extension able to load the configuration for “resource_owners”  —无法为“resource所有者”加载配置…这是啥情况

我用我的QQ

密码是md5密文形式的…..

    Chris Yue

    二月 27, 2018 在 4:38 下午

    你好。

    看错误信息的话,我怀疑是 config.yml 文件里面这一部分配置

    你是不是写成了

     

jack

九月 5, 2017 在 4:46 下午

有写配置 报错 check_qq routing.yml 那里 也不知道什么原因。我想你如果把源码放到了github上。我们能对照一下。谢谢博主啊。

    Chris Yue

    九月 8, 2017 在 11:07 下午

    我记得第一天的教程就放了源码的链接了吧……怕你们上 github 不快我还放 code.net 上的

     

jack

九月 1, 2017 在 1:58 下午

大哥你又github么 把源码放上去好不好。。。我composer 真的啥都装不上啊。

    Chris Yue

    九月 1, 2017 在 8:22 下午

    你不都知道用镜像了么……难道用了镜像还不快吗?

     

jack

九月 1, 2017 在 10:55 上午

jack

九月 1, 2017 在 10:52 上午

composer require “hwi/oauth-bundle:0.4.*@dev” 每次composer 都好慢啊。

Vector

二月 29, 2016 在 5:59 下午

你好,当我按照步骤全部做完的时候,访问如下网址

http://localhost/symfony2.8/web/news/new

会出现
Full authentication is required to access this resource.
500 Internal Server Error – InsufficientAuthenticationException
1 linked Exception:

AccessDeniedException »

请问这是什么原因呢

    Vector

    二月 29, 2016 在 6:01 下午

    程序不会自动跳转到登陆页面

     

    Chris Yue

    三月 6, 2016 在 12:06 上午

    anonymous: ~你在firewall里写了吗?

     

    jack

    九月 5, 2017 在 5:55 下午

    可能是防火墙顺序问题。把secured_area:放到第一个就好了

     

发表评论

÷ seven = one