授权码授权是 OAuth 2.0 中功能完整、流程严密、应用广泛的授权类型之一。
一句话概述:授权服务验证用户身份之后先发放授权码,客户端应用通过授权码访问授权服务获取访问令牌。因为客户端获取访问令牌的过程是在服务器后端进行,所以访问令牌不会暴露,具有较高的安全性。
授权流程
1. 流程示意图
+----------+
| 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)
2. 步骤解析
(A):客户端引导用户代理(浏览器)到达授权服务器的授权终结点,并携带参数:response_type
(code)、client_id
、redirect_uri
、scope
、state
。
(B):授权服务器通过用户代理(浏览器)验证资源所有者(用户),并确定资源所有者是否同意客户端授权。
(C):验证资源所有者成功,并且允许授权,授权服务器将用户代理(浏览器)重定向到 redirect_uri
,并返回 code
和授权请求的 state
。
(D):客户端向授权服务器令牌终结点请求令牌,并携带参数:grant_type
、code
、redirect_uri
、client_id
、secret
(可选)。
(E):授权服务器验证 code
、client_id
、secret
是否有效,并验证redirect_uri
是否与步骤(C)中一致,如果验证成功,携带 Access Token 将用户代理(浏览器)重定向到 redirect_uri
。
参数详解
response_type
:表示响应类型,必选项,此处授权码授权的值固定为code
。client_id
:表示客户端的ID,必选项。redirect_uri
:表示重定向URI,可选项。scope
:表示申请的权限范围,可选项。state
:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。用于防止恶意攻击。
案例解析
以第三方应用接入微信开放平台为例。
- 在第三方应用中点击“微信登陆”,跳转到微信用户授权页面
请求:
https://open.weixin.qq.com/connect/oauth2/authorize
?appid=APPID
&redirect_uri=REDIRECT_URI
&response_type=code
&scope=SCOPE
&state=STATE
参数:
appid
:网页应用唯一标识redirect_uri
:授权成功后重定向的回调链接地址,请使用 urlEncode 对链接进行处理response_type
:响应类型,值固定为code
scope
:授权范围,由微信公众平台定义,供用户选择的授权范围,有snsapi_base
、snsapi_userinfo
两种state
:由客户端生成,并在重定向Uri中会原样返回,用于验证回调请求的合法性,开发者可以填写 a-z A-Z 0-9的参数值,最多128字节。
响应:重定向
REDIRECT_URI
?code=CODE
&state=STATE
code 为微信公共平台返回的授权码。
- 通过 code 获取 token
请求:
https://api.weixin.qq.com/sns/oauth2/access_token
?appid=APPID
&secret=SECRET
&code=CODE
&grant_type=authorization_code
参数:
appid
::网页应用唯一标识secret
:网页应用密钥code
:通过上一步获取的 code 参数(存在有效期,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系)grant_type
:授权类型,值固定为authorization_code
响应结果:
{
"access_token":"ACCESS_TOKEN", //网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
"expires_in":7200, // access_token接口调用凭证超时时间,单位(秒)
"refresh_token":"REFRESH_TOKEN", //用户刷新access_token
"openid":"OPENID", //用户唯一标识
"scope":"SCOPE" //用户授权的作用域,使用逗号(,)分隔
}
access_token
:表示访问令牌,必选项。token_type
:表示令牌类型,该值大小写不敏感,必选项,可以是 bearer 类型或 mac 类型。expires_in
:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。refresh_token
:表示刷新令牌,用来获取下一次的访问令牌,可选项。scope
:表示权限范围,如果与客户端申请的范围一致,此项可省略。
特点
示意图中:步骤(A)(B)(C)在前端通道(Front Channel)进行,即通过用户代理(浏览器)实现用户与授权服务器交互;步骤(D)(E)在后端通道(Back Channel)进行,即在客户端(后台)与授权服务器授权终结点和令牌终结点进行交互。
在前端通道发送的交互请求采用 TSL 加密通信,确保请求不会被第三方篡改;在后端通道中执行的操作,第三方法无法拦截,确保获取授权令牌过程的安全。
适用场景
适用于有服务器端的应用程序,如:Web应用、移动应用(有服务端)。
思考
为什么在得到授权码(Authentication Code)之后,还要再访问一次,将授权码换成访问令牌(Access Token)?
因为授权码走前端通道,并不安全,可能被拦截。
这里涉及到两个概念:
- Front Channel 前端通道:浏览器/移动应用与服务器的通信,安全性相对较低。
- Back Channel 后端通道:服务器与服务器之间的通信,高度安全的。
通过前端通道获取授权码,然后通过高度安全的后端通道使用授权码获取访问令牌,确保访问令牌的安全发放。