一、什么是OAuth2.0协议

OAuth是一个开放授权,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户的账号密码提供给第三方移动应用。

OAuth是怎么做的呢?其实就是引入一个授权层,用来分离两种角色:客户端和资源所有者。授权层向第三方引用颁发令牌。客户端通过令牌去请求数据。

OAuth2.0的标准是RFC6749文件,是OAuth协议的延续版本,但是不向后兼容OAuth1.0。所以现在用的都是OAuth2.0

1.1 OAuth协议的实现流程

送上一张酷炫的流程图

OAuth2协议

1.2 为什么要使用OAuth2协议?

答:任何的身份认证都是基于对请求方的不信任造成的,比如上图中的豆瓣网,不信任用户浏览器的请求,但是信任QQ授权服务,所以只要QQ授权服务授权了,我就可以信任你的请求。

1.3 授权所需信息

向认证服务器请求认证时,需要哪些信息?

  • 应用名称
  • 应用网站
  • 重定向 UR I或者 URL
  • 客户端标识 client_id
  • 客户端密钥 client_secret

二、使用场景

  • 第三方的应用授权登录:在APP或网页接入一些第三方的应用时,通常都要用户登录另外一个合作的平台,比如QQ、微信、新浪、Github等的授权登录
  • 原生APP授权:app登录请求后台的接口,基于安全考量,所有的请求都需要携带token信息,如果登录验证、请求后台数据

三、OAuth2.0 的四种授权模式

OAuth 的核心就是向第三方应用颁发令牌。RFC6749文件 里规定了获得令牌的四种模式。

  • 授权码模式(authorization code)
  • 简化模式(implicit)
  • 密码模式(password)
  • 客户端凭证模式(client credentials)

3.1 授权码模式

授权码方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。

这是最常用的流程,安全性也最高,适用于有后端的web应用。

授权码模式

第一步:A网站提供一个链接(用QQ账户登录),用户点击后就会跳转B网站,目的是获取授权码

https://b.com/oauth/authorize?
  reponse_type=code&  # 返回类型,code表示授权码
  cilent_id=CLIENT_ID&  # 授权服务器记录的客户端id
  redirect_uri=CALLBACK_URL&  # 回调的地址
  scope=read  # 授权范围

第二步:用户跳转后,B网站会要求用户登录,然后询问是否同意授权A网站。用户同意后,B网站就会重定向到 redirect_uri 参数指定的网址,并且传回一个授权码(假设是123456),例如下面这样,code参数就是授权码

https://a.com/callback?code=123456

第三步:A网站拿到授权码后,就可以在后端,向B网站请求令牌

https://a.com/callback?
  client_id=CLIENT_ID&
  client_secret=CLIENT_SECRET&  # 客户端密钥,只有在后端请求时自己带上
  grant_type=authorization_code&  # 授权类型,授权码模式
  code=123456&   # 第二步拿到的授权码
  redirect_uri=CALLBACK_URL  # 令牌颁发后的回调地址

第四步:B网站收到请求后,颁发令牌。具体做法是向redirect_uri 指定的地址,发送一段json数据

{
  "access_token":"ACCESS_TOKEN",   # 颁发的令牌
  "token_type":"bearer",  # 令牌的类型
  "expires_in":2592000,  # 过期时间
  "refresh_token":"REFRESH_TOKEN",  # 用于令牌过期后,获取新的令牌
  "scope":"read",
  "uid":100101,
  "info":{...}
}

3.2 简化模式

有些WEB应用时纯前端应用,没有后端。所以就不适合使用授权码模式,RFC6749提供了第二种方式-简化模式,直接将令牌存在前端。

简化模式

第一步:A网站提供一个链接,要求用户跳转B网站,提供用户数据给A网站使用

https://b.com/oauth/authorize?
  response_type=token&  # token表示直接返回令牌
  client_id=CLIENT_ID&
  redirect_uri=CALLBACK_URL&
  scope=read

第二步:用户跳转B网站后,登录后同意授权。B网站就会跳转redirect_uri指定的网址,并传回令牌

https://a.com/callback#token=ACCESS_TOKEN

需要注意的一点,令牌的位置是URL锚点(fragment),而不是查询字符串(querystring)。就是上面地址里的#后面的内容,而不是?后面的内容。这是因为OAuth2.0允许跳转网址是HTTP协议,因此存在中间人攻击的风险,而浏览器跳转时,锚点是不会发到服务器的,这样就减少了令牌泄漏的风险。

3.3 密码模式

如果你高度信任某个应用,可以把用户名和密码直接告诉该应用。该应用就使用你的账号密码去申请令牌。这种方式用的比较少。

第一步:A网站要求用户提供B网站的用户名和密码。拿到以后,A网站直接向B网站请求令牌

https://oauth.b.com/token?
  grant_type=password&  # 密码模式
  username=USERNAME&
  password=PASSWORD&
  client_id=CLIENT_ID

第二步:B网站验证身份通过后,直接给出令牌。这里不需要跳转了,直接将令牌放在JSON数据里,作为HTTP响应。

3.4 客户端凭证模式

这种方式适用于没有前端的应用,并且不是针对用户的,极有可能是多个用户共享一个令牌。

第一步:A应用向B认证服务发出HTTP请求

http://b.com/token?
  grant_type=client_credentials
  scope=read

第二步:B验证通过后,直接返回令牌。

四、令牌的使用

拿到令牌后,每次请求都必须带上令牌。具体做法是在请求头里,加上一个 Authorization 字段,令牌放在这个字段里。

# header
Authorization:Bearer ACCESS_TOKEN

五、更新令牌

令牌如果有效期到了,还要用户再走一遍上面的流程,体验就太差了。OAuth2.0运行用户到期自动更新令牌。

具体做法:B网站颁发令牌时,颁发两个令牌,一个获取数据,一个用于获取新的令牌(refresh_token)。到期时,用户使用refresh_token发一个请求,更新令牌。

https://b.com/oauth/token?
  grant_type=refresh_token&
  client_secret=CLIENT_SECRET&
  refresh_token=REFRESH_TOKEN

B网站验证refresh_token通过后,就会颁发新的令牌了。