Open edX各种登录方式探索
文章目录
缘起
对异构系统的整合是我的兴趣之一,Open edX的开放式设计使它很容易与其他系统整合,其中包括用户系统的整合
前前后后折腾了edx的各种登录和注册机制,在此理一下。前几天对python-social-auth做了探索,也一并做下笔记
主要内容包括:
- CAS
- OAuth2
- OAuth2 client
- OAuth2 server
- 更改login机制,同时支持username/email登录
- QQ/微信登录
- 移动端跳转
CAS
关于CAS,我此前有写过一篇文章为什么CAS应该成为你的LMS的一部分
上边文章介绍了CAS的使用场景和它的原理,也给出了一些参考资料,对CAS不熟悉的小伙伴可以参考
看到这里就假设你基本弄懂CAS的原理啦,那我们直接讨论如何将CAS和edx对接
@MT说edx内置的cas client坑比较多,所以我试着改造了django-cas,这是改造后的:wwj718/django-cas,按照项目主页的引导,你就可以直接在Open edX里使用cas啦
具体的实现可以参考我的commit,代码很短,就几行
OAuth2
关于OAuth2的学习可以参考我的笔记:OAuth学习笔记
如果你对此熟悉 ,我们继续前进
CAS登录中,有一个中心,这个中心是CAS server,这种登录方式往往是集中式的。而OAuth2可以用于分布式登录的场景,尽管它的用途不只于此(还包括访问受限的资源)。
你一定使用过这种登录方式,许多网站都支持支持QQ/微信/google/facebook登录
这便使用了OAuth2
OAuth2 client
当我们访问网站A(比如我们的edx实例)时,使用了QQ登录,那么有以下事件发生
- 用户访问A网站,选择QQ登录,网站将用户导向qq认证服务器。
- 用户登录QQ,并选择是否给予网站A授权。
- 若用户给予授权,QQ认证服务器将用户导向A网站事先指定的"重定向URI"(redirection URI),同时附上一个授权码。
- A网站收到授权码,附上早先的"重定向URI",向QQ认证服务器申请令牌。这一步是在A网站的后台的服务器上完成的,对用户不可见。
- QQ认证服务器核对了授权码和重定向URI,确认无误后,向A网站发送访问令牌(access token)和更新令牌(refresh token)。
之后A网站在后端携带用户的access token,可能拿到用户资料了。至此A网站的后端可以知道用户A是否是A 网站的合法用户,对于采用QQ登录网站A而言,已经足够了。
如果你熟悉OAuth2,你会发现这就是使用最广泛的授权码模式
需要注意的是,本地用户系统如何与用户QQ资料挂钩,诸如展示用户名或者用户头像,这些不是oauth2该做的,即便走通了oauth2的流程只意味着,该用户在QQ认证服务器那边是合法用户,同时本地后台也可以拿到用户资料(QQ昵称/头像),可是如何把用户昵称和头像与网站A本地系统整合,好比username和昵称挂钩,或是与id挂钩,这需要在网站A注册系统里手写逻辑,一般在上边的最后一步做,注册完成之后,把用户重定向到dashboard。可以参考edx中既有的google/facebook/linkedin
你也可以参考这个案例:Logging-into-Django-w–Twitter,尽管这个案例和edx无关,但它基本把流程说清了
值得一提的事,edx整合外部登录到系统里的方式是采用用户绑定而不是直接注册,所以会导致以下问题
如果你使用python-social-auth,即便你好不容易,折腾半天,感觉已经没问题了,腾讯那边还会说点击QQ登录按钮提示登录失败或出现错误信息(无跳转、提示失败、出现错误信息)
,于是不让你审核通过,原因是edx的third_party_auth
并不会自动将oauth2登录通过的用户注册到edx里,而是要求你使用edx既有用户绑定一下,之后才可以使用qq登录。
这样一来腾讯那边认为你并没有登录成功,所以没法审核通过
以上是Open edx使用OAuth2 client登录qq的场景
最后需要郑重提醒的是网站基本信息里回调地址得是http:xxx/auth/complete/qq/
OAuth2 server
lms的 OAuth2 server 用的是django-oauth2-provider
下边我们讨论Open edX作为OAuth2 server的场景,这时Open edx相当于我们上边提到的QQ认证服务器的角色,此时B网站就可以使用Open edx的用户登录他们的网站,诸如insights就是这样做的,insights本身是个独立完整的网站,为了与lms/cms整合在一起,采用了OAuth2来关联用户,这时候lms就扮演了OAuth2 server的角色,只要你拥有lms的用户,就可以直接登录insights,用户的感觉是只有一个用户系统。
整个认证的流程和上边基本相同,具体的操作可以参考edX Analytics Installation
不同的地方主要是OAuth2 server(lms)和OAuth2 client(insights)都是自己的,所以OAuth2 client是受信任的client,这是一种客户端模式
,比前头提到的授权码模式来得简单,关于这些模式的对比,可以参考我的这篇文章:OAuth学习笔记
关于客户端模式的代码,参考enable Open edX REST APIs(work with mobile),通过客户端模式,我们可以轻易了解用户和密码的正确性
|
|
产生受信任客户端的核心就是我们能控制OAuth2 server,在其中执行:
sudo /edx/bin/python.edxapp /edx/bin/manage.edxapp lms --setting=aws create_oauth2_client http://insight:18110 http://insight:18110/complete/edx-oidc/ confidential --client_name insights --client_id YOUR_OAUTH2_KEY --client_secret secret --trusted
其中http://insight:18110/complete/edx-oidc/
是回调地址,这一步往往是两个平台用户关联的核心所在,有兴趣的同学可以直接翻源码,出于篇幅限制,在此不详述。这个url,来自python-social-auth
这里给我们的一个启示是:如果你想拓展edx,所做的拓展并不需要再界面上与edx整合(内嵌的整合可以用djangoapp/xblock),而又希望两者能看起来像一个系统(用户打通),那么采用insights的这种架构就很好,实际上,open edx的整个项目就是由若干服务组成的,edx本身的就够就是由若干异构系统拼成的,这个今天人气很高的微服务
有异曲同工之处
顺便再提一下,insights中有许多地方需要lms的数据,诸如题目统计里需要统计各类题目的正误情况,所以我们需要题目的信息,而这些信息insights里是不包括的,实际上这也是通过rest接口完成的,认证机制也是OAuth2,接口在这里Course Structure API
回到OAuth2 server的话题,lms作为OAuth2 server,我们首先需要启动它,通过在lms.env.json
里设置(FEATURES)
|
|
而在insight里,确保/edx/etc/insights.yml
(如果跑脚本的时候设置正确,这些会自动生成)
|
|
外部登录细节
我们从insights开始,我们把insights看做一个oauth2 client,登录入口为/accounts/login/
|
|
通信的过程是标准的oauth2登录方式,具体的请求地址可以参考:auth-backends
其中EdXOpenIdConnect是关键所在
相关请求url为:
- AUTHORIZATION_URL : http://LMS/oauth2/authorize/ //使用get获取code,有时效性
- ACCESS_TOKEN_URL : http://LMS/oauth2/access_token/ //使用post 携带参数获得access_token
- USER_INFO_URL : http://LMS/oauth2/user_info/
我们可以试试手动获取用户数据
|
|
以上采用的是受信任客户端的模式
这一块的单步调试非常错综复杂,是应为oauth2的url部分写得奇蠢无比。
|
|
官方采取了url覆盖的方法,而不是明确指出,以至于你如果不深入源码丛林就找不到url对应的view,而由于这些是外部库,所以ack也很不方便
我们只好求助一些工具: sudo /edx/bin/python.edxapp /edx/app/edxapp/edx-platform/manage.py lms show_urls --settings devstack | grep user_info
|
|
从中我们找到了user_info对于的方法,它来自edx_oauth2_provider,不要问题为何知道。。
如果你要手动处理oauth2,试试:requests-oauthlib,分步调试的话,可以看这个:examples/google,不过需要https
更多的调试细节,比如各个参数的含义,那么你需要了解oauth2协议本身,那样可以从http层面调试,否则会很艰难,还是尽量使用oauth2 client吧
如果你对过程参数感兴趣可以参考使用Authorization_Code获取Access_Token
对原理说的最清楚的为使用 OAuth 2.0 访问豆瓣 API
当携带code请求access_token是,使用http –form提交,例子如:
|
|
至于如何拿到code
|
|
|
|
文章作者 种瓜
上次更新 2016-04-12