从0开始设计Oauth2.0 - 授权码模式
Posted 开发者实验手册
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从0开始设计Oauth2.0 - 授权码模式相关的知识,希望对你有一定的参考价值。
摘要:当我们在网站上点击“使用qq登陆”/“微信登陆”发生了什么事情?如果我的网站也想使用第三方账号登陆,要怎样接入他们,接入的流程是怎样设计的?下面我们一起来探讨。
Oauth2.0的应用场景?
读者您好,假设您现在是公司的技术负责人,公司拥有用户的账户信息(头像,昵称等),拥有用户的相册信息,拥有用户的消费记录信息等。为了提升公司的影响力,您希望其他的一些公司也能利用这些信息。当然,这些数据都是用户的隐私数据,公司不能私下将数据发送给第三方,得先获得用户的授权。在设计授权策略的时候,您大概率会考虑到Oauth。Oauth 就是用于解决上述问题的一套标准协议,但这里暂不细说 Oauth 的内容,而先尝试探索其实现思路。
交互流程
于用户而言,授权流程应该是怎样的操作?
首先用户访问的是第三方网站,然后授权第三方网站获取授权资源,所以第三方网站先要设置授权入口。当用户点击授权按钮后,用户需要证明自己是“自己”,也就是需要用户登录自己的账号。登录过程中,第三方网站会告诉用户其申请了什么样的资源。最后,用户点击授权即完成这次授权。
以在知乎使用 QQ 登录为例:
在网站有授权入口,用户点击开始授权:
点击QQ的图标后打开了 QQ 的授权页面,输入 QQ 账号密码,过程中提示知乎网申请了 QQ 昵称、头像和性别信息:
点击【授权并登录】完成了授权,知乎网获得了用户的 qq 昵称、头像和性别信息。
知乎网进入克绑定手机的流程,这部分就和授权协议无关了。有些网站直接完成登录,有些需要绑定手机,有些需要填写身份信息,不同的网站会根据自身的业务而不同。
设计思路
命名
在思路设计之前,先明确几个定义:
- 像qq这样的资源拥有者下文称为“授权方”。
- 像知乎这样的第三方应用下文称为“网站应用”。
- qq的拥有者称为“授权用户”。
- 授权用户授权给第三方应用的信息称为“授权资源”。(昵称,头像等就是授权资源
- 上面图二,授权用户输入账号密码的页面称为“授权页面”。
- 下面还会提到“申请接入”的概念,具体下述。
思路
上面提到,使用qq登录的时候,知乎网会获得用户的昵称头像性别信息。除了这些信息,腾讯还开放了其他一些信息:
即设计授权流程的第一步就是设计一套 API ,规定第三方公司能访问什么资源,不能访问什么资源。
网站应用在获取授权信息前,授权方还需要对其进行资质审核。比如需要网站应用提供网站域名,网站用途等,然后审核的时候验证网站是否备案,网站用途是否和备案信息一致等。
以腾讯为例,腾讯设计了 https://connect.qq.com/ 开放平台,审核通过后,平台提供 appid (用于作为第三方应用的唯一标志)和 appkey(用途后面说明)。在开放平台填写申请的过程称为“申请接入”。
网站应用在请求授权资源的时候,需要打开授权页面,授权用户将在授权页面上完成授权操作。网站应用可以通过 <a> 标签直接跳转到授权页面,也可以通过 iframe 来嵌入,本质是向授权方发送一个 GET 请求。
GET 请求的参数该如何设计?
首先,当然要告知授权方当前的网站应用是“谁”?这个可以通过上面提到的 appid 来实现。在 GET 请求中,还要告知授权方授权资源是哪些。所以,现在构造的请求链接是:
https://graph.qq.com/oauth2.0/authorize?
client_id=${appid}&scope=${scope}
其中,
client_id 表示 appid,
scope 表示需要获取的授权资源,如申请用户信息的时候值为 get_user_info,申请相册信息的时候值为 list_album 等
那授权方要怎样将授权资源返回给网站应用?
直接在上面的 GET 请求中返回授权资源显然是不合理的,因为 appid 和 scope 可以轻而易举地在网站中获取,意味着可以轻而易举地伪造这个请求,授权资源会因此而泄漏。
为解决上述问题,可以将一个授权码返回给网站应用,然后网站应用再通过授权码来获取信息。由于网站应用是通过 <a> 标签或者通过 iframe 来打开授权页面的,当前页面属于资源方,所以授权完毕后不能直接返回数据,而是要将数据返回给授权网站。通过重定向并在重定向的 url 里带上要返回的参数可以实现这步操作。而重定向的路径在打开授权页面的时候就需要告诉授权方。所以,构造的链接就是:
https://graph.qq.com/oauth2.0/authorize?
client_id=${appid}&scope=${scope}&redirect_uri=${redirect_uri}
在授权完成后,浏览器会进行重定向,而重定向的 url 如下:
redirect_uri?code=${code}
http://www.example.com/oauth/callback?code=${code}
code 就是授权码。
在网站应用的服务器后台需要有处理 /oauth/callback 这个路由的方法。本质上,网站应用的服务器只需要将 code 发送给资源方即可。
那么资源方先要确保 code 是来自网站应用的服务器。当然可以在申请接入的时候就填写网站应用的IP列为白名单,但是这样对于网站应用来说太不灵活了。
上面提到申请接入完毕后,会获得 appid 和 appkey。这两个就相当于网站应用的账号和密码,资源方通过账号密码就可以鉴别应用服务器。
网站应用将 appid, appkey, code 发送给授权方后,授权方通过 appid 和 appkey 检测网站应用的身份是否合法,然后再通过 code 来判断授权是否合法。检查成功后,授权方不会直接返回授权资源,而是返回三个参数:能够访问授权资源的令牌 accesstoken,accesstoken的过期时间expiresin 和用于更新 accesstoken 的更新令牌refresh_token。
这样能够避免用户频繁授权,提升用户体验。用户只需要授权一次,网站应用获得了访问令牌并缓存下来,以后只需要利用访问令牌就可以访问授权资源。从安全的角度考虑,给accesstoken 设置有效时间,通过 refreshtoken 来更新令牌。
最后,网站应用通过 access_token 获取了授权资源,完成了整个流程。
上面提到的过程,就是 Oauth2.0 的授权码模式的原理。目前 qq,微信,是微博等都使用这个模式来开放资源。真正的实现方式所传递的参数和上述举例的会有些许不同,接入不同的授权方也会有所不同,比如请求 code 的时候,还需要带上 response_type 参数和 state 参数,具体的细节不做详述。
最后
以上是关于从0开始设计Oauth2.0 - 授权码模式的主要内容,如果未能解决你的问题,请参考以下文章