OAuth2.0-授权码模式

解决问题

OAuth2.0授权码模式主要解决了信任问题:一个第三方网站需要访问我们Github上的数据(例如用户头像),那Github为什么要信任该网站?该对网站信任到什么程度?

  • 如果彻底信任该网站,那么将Github的用户名和密码直接交给该网站,由该网站直接登录即可。但这样使得该网站登录等同于用户登录,该网站将拥有与用户相同的权限,Github无法做权限的区分。
  • 如果不再授权给该网站,则需要更改Github密码,这样也会影响到其他被授权的网站。

基于此,如果可以为该网站提供一个专门的access_token,该access_token有专门的权限和过期时间,且Github可随时清除access_token的授权,这样问题就可以解决了。
为了提供这样一个access_token,使用如下思路:

  1. 向Github登记一下该第三方网站,Github会给出一个凭据。记下该凭据。
  2. 第三方网站提供了Github授权按钮,通常是在登录页面提供。
  3. 用户点击授权按钮来同意Github授权,则携带凭据跳转到Github的管理页面,此时需登录。若已经登录则执行步骤4。
  4. 登录Github后,验证凭据,同意授权,则生成一个access_token返回。
  5. 第三方网站保存该access_token并向Github请求数据。
  6. Github返回数据,第三方网站使用这些数据匹配已有账号,匹配成功则登录;否则创建新账号并关联,然后登录。

流程

以Github授权给第三方网站为例,OAuth2.0的授权码模式流程如下:
OAuth2.0-授权码模式_第1张图片
这里将第三方网站的Web端和服务端分开,而非合并在一起。Github也是。这样更容易理解整个流程的细节及设计原因。
要使用授权码访问到服务器上的资源,有三个阶段:

  1. 登记应用。
  2. 获取access_token
  3. 资源访问。

登记应用

假设我们是第三方网站的开发者,我们开发的网站要访问Github上的数据(例如用户头像),那Github为什么要信任我们的网站呢?
于是就需要进行应用的登记:我们明确地告知Github我们的应用的必要属性,Github登记后,就认可我们的网站了。
登记的流程为:
登录我们的Github账号,在其管理中找到Developer settings进入,在OAuth Apps下点击New OAuth App来创建一个新的应用。
新建应用时,需填入如下的属性:

  • Application name: 应用名。
  • Homepage URL:应用的首页地址。
  • Application description: 应用描述。
  • Authorization callback URL: 授权回调地址。默认地址。若授权请求中没有附带redirect_uri参数,则使用该地址。

点击Register application即可注册成功,Github会为该应用生成唯一的client_idclient_secret属性,这就是申请access_token的凭据。
然后第三方网站需要将client_idclient_secret保存下来,通常是作为整个网站的全局配置。该网站所有的用户都将共享这2个属性。

获取access_token

流程

第三方网站的Web端登录页需要提供一个使用Github账号登录的按钮。点击该按钮,Web端向自己的服务端索要client_idstate,拿到后Web端拼接出Github的授权页面地址,直接以外链形式打开该授权页面。
这个外链地址虽然是Github的,但需要写在我们的第三方网站前端页面中,以作为外链来跳转。同时还需要在外连上直接以明文形式附加参数,主要包含:

  • response_type: 模式类型。必须。授权码模式固定为code
  • client_id: 应用的id。必须。在Github登记后由Github生成,用于Github识别应用。
  • redirect_uri: 重定向地址。可选。无论是否同意授权,Github都要调用该外链地址来跳转回第三方网站。如果不附带该参数,则Github会使用在注册应用时提交的Authorization callback URL
  • scope: 权限。可选。用于说明请求资源的范围,即Github允许第三方网站访问哪些资源。通常scope参数依赖于授权方的定义,这里是依赖Github的定义。
  • state: 校验码。可选。当Github调用重定向地址时会再次传回该参数,用于第三方网站对请求进行合法校验,从而防止CSRF攻击。因此第三方网站需要将state保存在本地,以用于后续的校验。

外链到Github的页面打开后,访问的就已经是Github的Web端了,若Github没有登录则会提示登录Github账号。登录后,需要在页面上点击[确认授权]按钮。
点击后,Github的Web端向Github的服务端发确认授权请求,服务端生成授权码code返回给Web端。然后Web端会将code和跳转页面时传入的state拼接到redirect_uri后,以https的外链形式在浏览器中进行跳转,这样浏览器页面就又跳转回第三方网站(跳回第三方网站是在回传code时)。
浏览器页面跳转后,第三方网站的Web端将codestate发给自己的服务端,服务端对state进行校验,确认是自己发出的。
然后第三方网站的服务端向Github的服务端发送申请access_token的请求。参数主要包含:

  • client_id: 应用的id。必须。登记时Github生成。
  • client_secret: 应用的秘钥。必须。登记时Github生成。
  • grant_type: 授权方式。必须。当前为授权码方式,传入code
  • code: 上一步Github发来的授权码。必须。
  • redirect_uri: 必须。用于校验与请求code时传入的redirect_uri是否一致,不是用来做页面重定向的

Github校验client_id+client_secretredirect_uri+code无误后,生成access_token,直接放入请求的Response中返回给第三方网站的服务端。为了处理access_token过期问题,通常还会一起返回一个refresh_token
第三方网站的服务端收到返回,取出access_token后,即可调用Github的接口来获取用户相关的信息。获取到信息后,首先判断是否已存在关联账户,若存在则直接登录;否则使用这些信息来创建新用户,然后登录。
由于授权码code是跟Github的具体用户相关的,因此生成的access_token也是跟用户相关的,多个用户的access_token不同。

为什么要借助授权码code来获取access_token

第三方网站发出第一次请求时获取到了一个授权码code,使用授权码code再次获取access_token,然后才使用access_token来获取用户信息。为何要加入一个授权码code,而不是直接第一次请求就返回access_token呢?
从上述流程分析,可知:

  • 第一次请求时需要用户点击Github的[确认授权]按钮,因此页面必须跳转到Github网站的Web页面。由于需要用户点击操作,就无法将结果直接放在Response中返回,因为用户可以等一会再点授权按钮,这个等待时间可长可短,放在Response中很可能用户还没点按钮就请求超时了。
  • 跳转到了Github的Web页面,授权后必须再跳回第三方网站的Web页面,这样就需要从Github的Web页面构造一个第三方网站的Web页地址以外链形式打开,同时将授权结果拼接到URL参数中,这意味着授权结果是暴露在公网上的。若直接返回access_token拼接在URL中显然会造成安全问题。
  • 生成access_token需要校验client_secret,但client_secret是个需要保密的值,直接以外链形式传输该属性会直接暴露在公网上,同样有安全问题。

综上,先通过client_id获取一个跟Github的具体用户相关的授权码code,然后由第三方网站的服务端使用client_id+client_secretredirect_uri+code这4个属性来请求Github获取access_token。由于获取的请求交给了服务端,也就不用再担心client_secretaccess_token暴露的问题。
某些网站为了更加安全,对code设置了有效期,例如5分钟内有效,且只能使用一次。
实际上,OAuth2.0的简化模式就是取消掉了code这一步,直接在第一次请求时的重定向地址中附加access_token 由于存在暴露风险,因此往往只用于安全性不高的场景,且另外有效期非常短。通常也不会发放refresh_token

资源访问

授权码模式用于Github授权账号登录到第三方网站。因此第三方网站拿到Github账号的信息后使用这些信息来创建新用户即完成了对Github必要的资源访问,只需要访问一次,不需要重复访问。
当然,第三方网站可以对access_token与新创建的账号进行绑定存储,并使用refresh_token进行有效期刷新,从而也可以重复地对Github进行资源访问。
但一般来说,这些访问请求都是由第三方网站的后端负责,第三方网站的前端是不接触access_token的。

你可能感兴趣的:(Spring-Boot,java,OAuth2.0,授权码)