OAuth2的四种认证模式

7/25/2024 6:05:07 PM
301
0

一、授权模式

1.1、授权码模式(authorization code)

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

1.2、简化模式(implicit)

有些 Web 应用是纯前端应用,没有后端。这时就不能用上面的方式了,必须将令牌储存在前端。RFC 6749 就规定了第二种方式,允许直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)“隐藏式”(implicit)。

1.3、密码模式(resource owner password credentials)

如果你高度信任某个应用,RFC 6749 也允许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为"密码式"(password)。

1.4、客户端模式(client credentials)

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

二、授权码模式详解

授权码模式(authorization code)是功能最完整、流程最严密安全的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。

注意这种方式适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。

微信公众号授权流程即采用这种方式。

2.1 授权码模式流程如下

1)用户访问客户端,客户端将用户导向认证服务器。

2)用户选择是否给予客户端授权。

3)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码(每个用户的授权码不同)。

4)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的 后台服务器 上完成的,对用户不可见。

5)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

从上述的流程描述可知,只有第 2 步需要用户进行授权操作,之后的流程都是在客户端的后台和认证服务器后台之前进行"静默"操作,对于用户来说是无感知的。

下面是上面这些步骤所需要的参数。

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

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

示例

A 网站提供一个链接,用户点击后就会跳转到 B 网站,授权用户数据给 A 网站使用。下面就是 A 网站跳转 B 网站的一个示意链接:

https://b.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read 

上面 URL 中:

response_type参数表示要求返回授权码(code);

client_id参数让 B 网站知道是谁在请求;

redirect_uri参数是 B 网站接受或拒绝请求后的跳转网址;

scope参数表示要求的授权范围(这里是只读)。

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

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

示例

在第 2 步骤用户表示同意之后,这时 B 网站就会跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码,就像下面这样。

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

上面 URL 中,code参数就是授权码。

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

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

示例

在第 3 步骤中,A 网站拿到授权码以后,就可以在后端,向 B 网站请求令牌。

https://b.com/oauth/token?
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
 grant_type=authorization_code&
 code=AUTHORIZATION_CODE&
 redirect_uri=CALLBACK_URL 

上面 URL 中:

client_id参数和client_secret参数用来让 B 确认 A 的身份(client_secret参数是保密的,因此只能在后端发请求);

grant_type参数的值是AUTHORIZATION_CODE,表示采用的授权方式是授权码;

code参数是上一步拿到的授权码;

redirect_uri参数是令牌颁发后的回调网址。

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

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

 

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
 
{"access_token":"ACCESS_TOKEN","token_type":"bearer","expires_in":2592000,"refresh_token":"REFRESH_TOKEN","scope":"read","uid":100101,"info":{...}
} 

后续

当客户端(第三方应用程序)拿到访问资源服务器的令牌时,便可以使用这个令牌进行资源访问了。例如:拿到了access_token  和user_id (id_token之类的)请求用户详细数据

如何发送给资源服务器这个问题并没有在 RFC6729 文件中定义,而是作为一个单独的 RFC6750 文件中独立定义了。这里做以下简单的介绍,主要有三种方式如下:

  1. URI Query Parameter
  2. Authorization Request Header Field
  3. Form-Encoded Body Parameter

Authorization Request Header Field,因为在HTTP应用层协议中,专门有定义一个授权使用的Request Header,所以也可以使用这种方式:

GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM 

其中"Bearer "是固定的在access_token前面的头部信息。

令牌的刷新

为了防止客户端使用一个令牌无限次数使用,令牌一般会有过期时间限制,当快要到期时,需要重新获取令牌,如果再重新走授权码的授权流程,对用户体验非常不好,于是 OAuth 2.0 允许用户自动更新令牌。

具体方法是,B 网站颁发令牌的时候,一次性颁发两个令牌,一个用于获取数据,另一个用于获取新的令牌(refresh token 字段)。令牌到期前,用户使用 refresh token 发一个请求,去更新令牌。

2.2 隐藏式

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

https://b.com/oauth/authorize?
  response_type=token&
  client_id=CLIENT_ID&
  redirect_uri=CALLBACK_URL&
  scope=read

上面 URL 中,response_type参数为token,表示要求直接返回令牌。

第二步,用户跳转到 B 网站,登录后同意给予 A 网站授权。这时,B 网站就会跳回redirect_uri参数指定的跳转网址,并且把令牌作为 URL 参数,传给 A 网站。

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

上面 URL 中,token参数就是令牌,A 网站因此直接在前端拿到令牌。

注意,令牌的位置是 URL 锚点(fragment),而不是查询字符串(querystring),这是因为 OAuth 2.0 允许跳转网址是 HTTP 协议,因此存在"中间人攻击"的风险,而浏览器跳转时,锚点不会发到服务器,就减少了泄漏令牌的风险。

 

全部评论



提问