详解 OAuth2.0 协议

2019-07-31 16:42:10   最后更新: 2019-07-31 16:42:10   访问数量:91




只要是对结果第三方公共平台,都不会对 OAuth2.0 协议感到陌生,他是目前最为流行的授权机制,用来授权第三方应用在平台上进行某些受限的操作

那么,OAuth2.0 存在的意义是什么,又是怎么样的一种授权机制呢?本文我们就来详细介绍一下

 

现在我们正在使用一个阅读 APP,他可以从你的网盘的某个指定目录中直接获取其中保存的所有电子书

那么,这个 APP 需要登录到我们的网盘中,最简单的方法是我们在 APP 中通过账号密码登录我们的网盘,此时,APP 可以将我们的账号和密码保存甚至上传,这是十分不安全的一件事,因为拥有我们账号密码的 APP 可以对我们的网盘进行一切操作,但我们并不知道,甚至 APP 可以将我们的账号密码泄露出去,后果不堪设想

就算我们完全信任这个 APP,但如果我们需要使用很多个这样的 APP,某天我们想收回授权,唯一要做的只能是更改密码,但这样做以后所有的 APP 都无法再访问我们的云盘,这是极为不利于管理的

 

上面我们就阐述了通过共享账号密码给第三方实现授权存在的问题:

  1. 安全性问题
  2. 无法记录授权后执行的行为
  3. 无法限制被授权第三方应用的操作
  4. 多个授权不便于管理

 

解决上面的问题,我们可以引入令牌机制:

  1. 第三方应用提示要求授权
  2. 用户确认后,跳转到网盘登录页面,登录网盘
  3. 登录成功后,跳转回第三方应用时,网盘传递授权令牌给第三方应用
  4. 第三方应用获取到令牌后,可以通过令牌作为凭证来进行所需要的操作

 

上述的过程中,与上文所描述的账号密码授权有着显著的不同:

  1. 用户只在网盘提供的登录页面上进行登录,无需担心账号密码泄露给第三方
  2. 网盘提供给第三方应用的令牌的同时,可以在服务端存储该令牌对应的一系列信息,如第三方应用的各项信息,用户可以随时查询到自己已授权的第三方应用有哪些,甚至可以做到随时撤销某个第三方应用的授权
  3. 令牌通常是短期的,到期自动失效,第三方应用在此后将失去所有授权,而用户的账号密码则长期有效
  4. 网盘服务同样可以保存各令牌对应的权限、允许操作的目录等信息,严格限制第三方应用的访问权限

 

这就是 OAuth 标准的基本思想

令牌的出现,让平台给第三方应用的授权做到了随时可控,保证了用户信息和平台本身的安全

 

RFC6749(https://tools.ietf.org/html/rfc6749)详细描述了 OAuth2.0 标准的整个授权过程

 

OAuth 引入了一个授权层,用来分离两种不同的角色:客户端和资源所有者

整个过程非常简单:

 

 

  1. 客户端先去请求资源所有者
  2. 资源所有者确认后,客户端通过授权请求授权服务器,授权服务器向客户端颁发令牌
  3. 客户端从资源请求受保护资源,使用令牌验证身份
  4. 资源服务器向授权服务器验证令牌,验证成功则允许客户端进行操作

 

其中最为重要的环节就是令牌的颁发

RFC-6749 协议中,规定了四种获取令牌的方式:

  • 授权码
  • 隐藏式
  • 密码式
  • 客户端凭证

 

授权码方式是最常用的令牌颁发流程,流程相对复杂,但安全性是四种方式中最高的

通常我们使用的授信服务基本上都是通过这种方式来颁发令牌的

 

 

上图展示了整个令牌颁发的时序,存在以下交互过程

  1. 用户允许第三方客户端发起授权流程
  2. 第三方客户端通过 302 重定向到提供资源服务的授权服务器上进行登录授权,在这一步流程中,客户端需携带验证成功后的回调地址 redirect_url 与自己的 client_id 参数
  3. 授权服务器在校验客户端信息后给出用户相应的提示后跳转到登录页面来确认用户身份
  4. 用户登录确认身份后,授权服务器返回跳转到 redirect_url 的重定向请求,并携带有授权码
  5. 随着用户请求重定向,第三方服务端获取到了授权码,此时,第三方服务端通过预先已注册的受信 client_id 与 client_secret 以及获取到的授权码再次请求授权服务器
  6. 授权服务器进行第三方身份的验证后,颁发令牌并返回

 

在这种方式中,用户、第三方服务器、授权服务器三方进行了三次握手,并在三方中传递了授权码,最终授权码校验、颁发令牌的流程可以保证第三方服务所持有的用户授权的可信度,因此极大的保证了授权的安全性

 

有一些 web 应用时纯前端应用,没有后端服务器,此时显然不能使用上述授权码的流程来颁发令牌了

OAuth2.0 标准同样允许直接向前端颁发令牌,因为这种方式隐藏了授权码获取的相关步骤,因此被命名为“隐藏式”

 

 

正如上文所述,我们看到,隐藏式颁发令牌的流程时序图与授权码的方式十分接近,只是省去了第三方服务器通过授权码获取令牌的流程,取而代之的,在前面一步传递授权码时直接颁发令牌

由于整个过程都是在 HTTP 协议之上进行的,既然隐藏式是为了解决第三方客户端是纯前端应用的场景,那么,通过锚点(Fragment)传输令牌而不是通过参数传输就会更加安全,因为在 HTTP 协议中,锚点(Fragment)的跳转是由浏览器控制的,浏览器并不会将锚点传递给服务器,从而避免了令牌在请求中的传递

HTTP/1.1 302 Found

Location: http://techlog.cn#access_token=ACCESS_TOKEN

 

这种方式把令牌直接传给前端,是很不安全的,因此,只能用于一些安全要求不高的场景,并且令牌的有效期必须非常短,通常就是会话期间有效,浏览器关掉,令牌就失效了

微信小程序等这些纯前端的应用通常都是通过这种方式获取授权的

 

如果你高度信任某个应用,且上述两种方法都因为各种原因无法使用,那么,OAuth2.0 标准允许用户直接将让第三方应用持有用户的用户名和密码来申请令牌

交互流程十分简单,第三方应用只需通过用户的用户名、密码及自身的 client_id 作为参数请求,授权服务器直接在请求响应体中返回 access_token

POST /token HTTP/1.1

Host: server.example.com

Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

Content-Type: application/x-www-form-urlencoded

grant_type=password&username=johndoe&password=A3ddj3w

 

这是最不安全的一种授权方式,平台服务通常也都不提供这种授权方式

不过,如果授权服务器建立前,资源服务器已经长期被第三方应用直接使用用户的用户名和密码来进行访问,那么,暂时通过密码式的授权方式接入到授权服务器,此后再逐步更改为上述两种授权方式,将是一种代价比较小的迁移方式

 

最后一种方式是凭证式,适用于没有前端的命令行应用,即在命令行下请求令牌

第三方应用直接向授权服务器发送已受信的客户端凭证,与密码式一样,授权服务器直接在响应体中返回令牌

POST /token HTTP/1.1

Host: server.example.com

Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials

 

这种授权方式不针对某个用户,通常是授权给平台信任的应用,例如需要获取平台用户数等基本信息等

 

获取到令牌后,第三方应用就可以去请求资源服务器上已被授权的资源了,只需在每次请求的 header 中都带有令牌即可:

curl -H "Authorization: Bearer ACCESS_TOKEN" "https://resource.techlog.cn"

 

令牌有效期到了以后,如果让用户再次重复上述流程来申请新的令牌,显然非常繁琐而体验不好

OAuth2.0 允许第三方应用自动更新令牌

授权服务器在 access_token 下发时,一并下发了另一个令牌 -- refresh_token,他就是用来更新令牌的

在 access_token 失效前,第三方应用可以使用 refresh_token 请求授权服务器来获取新的令牌:

POST /token HTTP/1.1

Host: server.example.com

Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,全部原创,只有干货没有鸡汤

 

 

https://tools.ietf.org/html/rfc6749

 






技术分享      http      授权      oauth     


京ICP备15018585号