此篇文章同时发布在知乎专栏 前端后端客户端,专栏专注于前端、后端、客户端开发的技术分享与探讨,欢迎关注。

OAuth 2.0

  • OAuth 是一种关于授权(authorization)的开放网络标准
  • OAuth 目前的版本是 2.0

应用场景

以微信举例。

假设我们的项目有一个微信公众号服务。当用户进入我们的业务页面时,我们想获得用户的微信账号基本信息,从而对用户的一些行为进行记录。

若要获取用户的微信账号信息,就需要用户的同意,即获得用户授权。只有用户同意了,微信才能允许我们读取用户的基本信息。那么要如何获得用户授权呢?

如果没有 OAuth 2.0,传统的做法是用户把微信账号信息给我们,然后我们登录微信号来获取用户的基本信息。这听起来就十分傻*,也存在许多安全隐患。

  1. 业务方为了后续的业务需求,会记录用户的账号密码,非常不安全
  2. 除了获取用户的基本信息,业务方还能获得该账号拥有的其他权限,比如乱给别人发消息
  3. 只有用户修改了密码才能回收此授权

OAuth 就是为了解决上述问题而诞生的。

核心思想

  • 在“客户端”与“服务提供商”之间设置了一个授权层(authorization layer)
  • “客户端”无法直接登录“服务提供商”,但可以登录此“授权层”
  • 登录“授权层”时使用令牌 Token,该令牌与账号密码不同,拥有特定的权限范围与授权时间

运行流程

+--------+                               +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
  • (A)用户打开客户端以后,客户端要求用户给予授权
  • (B)用户同意给予客户端授权。(核心步骤)
  • (C)客户端使用上一步获得的授权,向认证服务器申请令牌
  • (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌
  • (E)客户端使用令牌,向资源服务器申请获取资源
  • (F)资源服务器确认令牌无误,同意向客户端开放资源

授权模式

OAuth 2.0 定义了以下 4 种授权模式:

  1. 授权码模式(authorization code)
  2. 简化模式(implicit)
  3. 密码模式(resource owner password credentials)
  4. 客户端模式(client credentials)

微信使用的是授权码模式(authorization code),因此我们重点介绍一下该模式。

授权码模式

+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)

步骤如下:

  • (A)用户访问客户端,后者将前者导向认证服务器
  • (B)用户选择是否给予客户端授权
  • (C)假设用户给予授权,认证服务器将用户导向客户端事先指定的“重定向 URI”(redirection URI),同时附上一个授权码
  • (D)客户端收到授权码,附上早先的“重定向 URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见
  • (E)认证服务器核对了授权码和重定向 URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)

客户端申请认证 URI

A 步骤中,客户端申请认证的 URI,需要包含以下参数:

  • response_type:表示授权类型,必选项,此处的值固定为 code
  • client_id:表示客户端的 ID,必选项
  • redirect_uri:表示重定向 URI,可选项
  • scope:表示申请的权限范围,可选项
  • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值

举个例子,微信的认证 URI 如下:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

服务器回应客户端的 URI

C 步骤中,服务器回应客户端的URI,包含以下参数:

  • code:表示授权码,必选项。该码的有效期应该很短,通常设为 10 分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端 ID 和重定向 URI,是一一对应关系
  • state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数

如果用户同意授权将会跳转到这个 URI:

redirect_uri/?code=CODE&state=STATE。

申请令牌的请求

D 步骤中,客户端向认证服务器申请令牌的 HTTP 请求,包含以下参数:

  • grant_type:表示使用的授权模式,必选项,此处的值固定为 authorization_code
  • code:表示上一步获得的授权码,必选项
  • redirect_uri:表示重定向 URI,必选项,且必须与A步骤中的该参数值保持一致
  • client_id:表示客户端 ID,必选项

看一下微信的:

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

来自认证服务器的 HTTP 回复

E 步骤中,认证服务器发送的 HTTP 回复,包含以下参数:

  • access_token:表示访问令牌,必选项
  • token_type:表示令牌类型,该值大小写不敏感,必选项,可以是 bearer 类 型或 mac 类型
  • expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间
  • refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项
  • scope:表示权限范围,如果与客户端申请的范围一致,此项可省略

看一下这部分微信的返回:

{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE"
}

微信授权

上面介绍了 OAuth 2.0 的原理和基本流程,以及每个步骤中的具体请求参数与这些步骤中微信的用户授权示例。下面就具体来看看在微信授权的过程中服务端具体需要提供哪些接口。

先附上微信官方文档:微信网页授权

服务端 API

授权接口

接口描述

用户同意授权后,用于从服务提供商获取 code。请求微信的 用户同意授权,获取code 接口。

接收参数
字段 类型 描述
target_uri string 最终要跳转到的业务页面地址
接口返回值

无返回值,获取 code 后跳转到授权回调接口地址。

授权回调接口

接口描述

上述授权接口获取到 code 后将跳转到该接口。

该接口目的:

  1. 通过 code 获取用户 access_token:通过code换取网页授权access_token
  2. 使用 access_token 拉取用户数据:拉取用户信息(需scope为 snsapi_userinfo)
  3. 根据业务需求,对用户数据进行操作,例如:入库、更新用户数据等
  4. 跳转到目标业务页 target_uri
接收参数
字段 类型 描述
code string 授权码
target_uri string 最终要跳转到的业务页面地址
接口返回值

无返回值,将重定向到目标业务页 target_uri


差不多先这样吧,后面会补上相关接口代码~

参考资料