文章

OAuth 2.0 简单介绍

是什么

OAuth 2.0 是目前互联网上非常流行的一种授权机制,它的工作原理是将用户身份验证委托给托管用户帐户的服务并授权第三方应用程序访问该用户帐户。在几乎所有主流的大型互联网产品中都有应用。例如,在登录掘金时,可以选择微博、微信或者 Github 这些第三方账号登录,这里的第三方登录使用的就是 OAuth 2.0 的授权机制。

角色

在 OAuth 标准中,定义了以下四个角色:

  • Resource Owner: 资源拥有者,即用户
  • Client: 想要获取用户信息的客户端(第三方应用),获取用户信息前必须经过用户授权
  • Resource Server: 存放用户信息的资源服务器
  • Authorization Server: 签发以及验证 token 的授权服务器

实际应用场景中,一般 Resource Server 和 Authorization Server 是结合在一起的,可以认为是一个服务同时提供了授权功能和获取用户资源的功能。

以掘金的第三方登录为例子,四个角色具体的对应关系如下:

  • Resource Owner: 掘金用户(你或者我)
  • Client: 掘金
  • Resource Server: 微博、微信、Github 提供的资源 API
  • Authorization Server: 微博、微信、Github 提供的授权 API

授权的流程

授权流程中,各个角色之间的交互流程如下图:

步骤解释:

  1. 应用向用户请求授权访问服务资源
  2. 如果用户授权了请求,应用会收到一个授权码
  3. 应用通过提供自身的身份验证(实际应用中一般是 CLIENT_ID 和 SECRET)和用户的授权码,从授权服务器 (API) 请求 Access Token
  4. 应用身份已通过身份验证并且授权码有效,则授权服务器 (API) 会向应用程序颁发 Access Token,授权完成
  5. 应用通过 Access Token 向资源服务器请求用户数据
  6. 如果 Access Token 有效,则资源服务器 (API) 将资源返回给应用

实际上 OAuth 有四种不同的授权方式,因此实际的流程也会因为使用不同的授权方式而有所不同,但这个流程就是 OAuth 的整体思路,可以看出 OAuth 的核心就是向第三方应用颁发 token,四种不同的授权方式也就是指四种不同的颁发 token 的方式。

准备工作

应用注册

在讲 OAuth 引入到应用之前,需要向你准备使用的服务提供你的应用信息,一般包括:

  1. 应用名称
  2. 应用网站
  3. 重定向地址或者回调地址

重定向地址是第三方服务在用户授权(或拒绝)你的应用后重定向的位置,它会通过这个地址回传授权码。

CLIENT_ID、SECRET

向第三方服务提供完信息即注册成功后,第三方服务一般会给你颁发一个 CLIENT_IDCLIENT_SECRET,它们是成对出现的,CLIENT_ID 是用来标识你的应用的,CLIENT_SECRET 则是在请求用户数据时用来验证应用身份的,它必须保存在服务端不能公开。

四种授权方式

授权码(authorization-code)

授权码是最常用的一种方式,这种方式需要先获取一个授权码,再通过授权码换取 Access Token,其安全性高,因为所有获取用户数据的操作都是在服务端完成的,Access Token 也是存储在服务端,因此适用于有后端服务的 web 应用。

1-3. A 应用向用户授权一个链接,链接大概长这样: https://xxx.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read 这个链接包含四个参数:

  • response_type=code: 表示要求返回授权码
  • client_id=CLIENT_ID: 告诉授权服务器请求来源
  • redirect_uri=CALLBACK_URL: 告诉授权服务器将授权码回调到哪个地址
  • scope=read: 表示授权的范围,read 表示只读

4-5. 用户授权 A 应用 用户点击了第一步的链接之后,会跳转到授权服务的登录页面,登录之后即表示用户授权成功,授权服务会带着授权码重定向到 CALLBACK_URL。大概长这样: https://aaa.com/callback?code=AUTHORIZATION_CODE,这个地址一般是前端页面,前端接收到 AUTHORIZATION_CODE 之后再传给后端服务获取具体的用户数据。

6-9. 后端服务用 AUTHORIZATION_CODE 加上 CLIENT_ID 和 CLIENT_SECRET 去换取 Access Token,获取 token 的请求大概长这样: https://xxx.com/oauth/token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=CALLBACK_URL 授权服务验证通过之后便会将 Access Token 回调到 CALLBACK_URL,一般会发送 JSON 格式的数据,大概长这样:

1
2
3
4
5
6
7
8
9
10
11
12
{
    "access_token": "ACCESS_TOKEN",
    "token_type": "bearer",
    "expires_in": 2592000,
    "refresh_token": "REFRESH_TOKEN",
    "scope": "read",
    "uid": 100001,
    "info": {
        "name": "White",
        "email": "white.hcj@gmail.com"
    }
}

至此,授权流程就完成了,成功获取到了 Access Token,接下来就可以通过 Access Token 调用资源服务器的 API 获取用户数据了。

隐藏式(implicit)

隐藏式是应用于纯前端应用的,没有了获取授权码这个步骤,由前端直接向授权服务器获取 Access Token。 对比授权码方式和隐藏式的流程图就可以发现,隐藏式其实就是授权码方式的简化版,没有了后端服务这个角色,因此其安全性也得不到保证,只能应用于一些对安全要求不高的场景,并且令牌有效期非常短。

密码式(password)

密码式要求应用在请求授权时需要带上用户的账号和密码,授权服务器验证通过后直接返回 Access Token。这种授权方式适用于用户高度信息该应用的场景,因为需要用户提供授权服务的账号和密码。

客户端凭证(client credentials)

客户端凭证这种方式是直接使用 CLIENT_ID 和 CLIENT_SECRET 获取 Access Token。这种方式获取到的 Token 是针对应用的,而不是用户的,这种场景比较少见。

Access Token 的使用

当应用程序通过上述的任意一种方式获取到 Access Token 之后,它就可以使用 Access Token 通过 API 获取用户的信息了,但是必须在有效期内而且仅能获取指定的访问范围的信息。请求大概长这样:

1
curl -X POST -H "Authorization: Bearer ACCESS_TOKEN" "https://xxx.com/xx-info"

Access Token 的更新

当 Access Token 过期后就无法使用其获取用户信息了。此时,如果在颁发 Access Token 时有返回 Refresh Token,则可以使用它从授权服务器请求新的访问令牌。请求大概长这样:

1
https://xxx.com/oauth/token?grant_type=refresh_token&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&refresh_token=REFRESH_TOKEN
本文由作者按照 CC BY 4.0 进行授权