理解OAuth 2.0并实现一个客户端 | 青训营笔记(上)

简介: 理解OAuth 2.0并实现一个客户端 | 青训营笔记(上)

前言

记录加入青训营的每一天笔记。

OAuth 2.0是一个关于授权的开放网络标准,主要致力于简化客户端人员开发,同时为Web应用程序、桌面应用程序、移动电话和物联网设备提供特定的授权规范。

简单来说,我们平时使用的很多第三方登录并获取头像等信息就是用的OAuth 2.0。如我们用QQ登录一些论坛,用google账号登陆facebook,用github账号登陆gitlab等。如下图展示的就是利用QQ登录网易云音乐Web版,其中用到的就是OAuth 2.0

理解OAuth 2.0

在开始编程前,我们先要掌握OAuth 2.0认证的流程是什么样的,下面我们一起走进OAuth 2.0

下面的情景都以我用QQ登录网易云Web版为例子。

相关名词

在理解OAuth 2.0前,我们先需要理解下面这几个名词。

  • resource owner: 资源所有者。也就是例子中的我(用户)。
  • resource server: 资源服务器,即服务提供商存放用户资源的服务器。例子中就是QQ的资源服务器,存放了我的QQ昵称,QQ头像,性别等等信息。
  • client: 客户端,需要得到资源的应用程序。例子中的网易云音乐Web版。
  • authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。例子中QQ提供的认证服务器(跳出来让我们扫码登录QQ的这个服务)。
  • user-agent:用户代理。我们用来访问客户端的程序,如这里就是浏览器。

总体说来,这种登录流程大概就是这样:

(resource owner)需要用QQ(resource server) 在浏览器(user-agent)登录网易云音乐(client),我们先登录QQ(authorization server),授权给网易云。然后网易云才能拿着这个授权信息去QQ资源服务器(resource server)获取到我的头像/昵称/性别等信息。

具体流程我们继续往下看。

协议流程

下图来自RFC6749

+--------+                               +---------------+
|        |--(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 ---|               |
+--------+                               +---------------+
                Figure 1: Abstract Protocol Flow
  • (A) 客户端请求resource owner授权;
    这种授权可以是直接向resource owner请求,也可以通过authorization server间接请求。例子中,就是跳转到QQ的authorization server,让用户登录QQ。
  • (B) 用户同意给予授权;
    例子中,用户点击同意,重定向回网易云音乐,这是一种授权模式。在RFC中规定了4中授权模式,具体采用哪一种取决于authorization server,下一小节具体介绍。
  • (C) 客户端拿着上一步的授权,向authorization server申请令牌(access_token);
  • (D) authorization server确认授权无误后,发放令牌(access_token);
  • (E) 客户端拿着令牌(access_token)到resource server去获取资源;
    例子中,获取QQ头像,昵称等。
  • (F) resource server确认无误,同意向客户端下发受保护的资源。

下面详细说到其中四种授权模式。

授权模式

客户端要得到令牌(access_token), 必须需要得到用户的授权,在OAuth 2.0 中定义了四种授权模式:

  • 授权码模式 (Authorization Code)
  • 简化模式 (Implicit)
  • 密码模式 (Resource Owner Password Credentials)
  • 客户端模式 (Client Credentials)

每种模式的使用场景与流程都有一定的差别。

授权码模式 (Authorization Code)

授权码模式的授权流程是基于重定向。我们例子中的用QQ登录网易云就是这种模式,流程图如下(来自RFC6749):

+----------+
  | 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)
Note: The lines illustrating steps (A), (B), and (C) are broken into
two parts as they pass through the user-agent.
                  Figure 3: Authorization Code Flow
  • (A) 用户访问客户端,客户端将用户重定向到认证服务器;
  • (B) 用户选择是否授权;
  • (C) 如果用户同意授权,认证服务器重定向到客户端事先指定的地址,而且带上授权码(code);
  • (D) 客户端收到授权码,带着前面的重定向地址,向认证服务器申请访问令牌;
  • (E) 认证服务器核对授权码与重定向地址,确认后向客户端发送访问令牌和更新令牌(可选)。

1)在A中,客户端申请授权,重定向到认证服务器的URI中需要包含这些参数:

参数名称 参数含义 是否必须
response_type 授权类型,此处的值为code 必须
client_id 客户端ID,客户端到资源服务器注册的ID 必须
redirect_uri 重定向URI 可选
scope 申请的权限范围,多个逗号隔开 可选
state 客户端的当前状态,可以指定任意值,认证服务器会原封不动的返回这个值 推荐

RFC6749中例子如下:

GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
     &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
 Host: server.example.com

我们在用QQ登录网易云音乐时实际的地址如下:

https://graph.qq.com/oauth2.0/show?which=Login
        &display=pc
        &client_id=100495085
        &response_type=code
        &redirect_uri=https://music.163.com/back/qq
        &forcelogin=true
        &state=ichQQlAgNi
        &checkToken=sretfyguihojpr

我们看到参数基本一致,至于还多几个,有什么用这里就没有去深究了。

2)在C中,认证服务器返回的URI中,需要包含下面这些参数:

参数名称 参数含义 是否必须
code 授权码。认证服务器返回的授权码,生命周期不超过10分钟,而且要求只能使用一次,和A中的client_id,redirect_uri绑定。 必须
state 如果A中请求包含这个参数,资源服务器原封不动的返回。 可选

如:

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
          &state=xyz
  1. 在D中客户端向认证服务器申请令牌(access_token)时,需要包含下面这些参数。
参名称 参数含义 是否必须
grant_type 授权模式,此处为authorization_code 必须
code 授权码,C中获取的code 必须
redirect_uri 重定向URI,需要和A中一致。 必须
client_id 客户端ID,与A中一致。 必须

如:

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
  1. 在E中,认证服务器返回的信息中,包含下面参数:
参数名称 参数含义 是否必须
access_token 访问令牌 必须
token_type 令牌类型,大小写不敏感。例如 Bearer,MAC。 必须
expires_in 过期时间(s), 如果不设置也要通过其他方法设置一个。 推荐
refresh_token 更新令牌的token。当令牌过期的时候,可用通过该值刷新token。 可选
scope 权限范围,如果与客户端申请范围一致,可省略。 可选

如:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}
  1. 如果我们的令牌过期了,需要更新,这里就需要使用refresh_token获取一个新令牌了。此时发起HTTP请求需要的参数有:
参数名称 参数含义 是否必须
grant_type 授权类型,此处是refresh_token 必须
refresh_token 更新令牌的token。 必须
scope 权限范围。 可选

如:

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA

这就是授权码模式,应该也是我们平常见的较多的模式了。

简化模式 (Implicit)

简化模式,相当于授权码模式中,C步骤不再通过客户端,直接在浏览器(user-agent)中向认证服务器申请令牌,认证服务器不再返回授权码,所有步骤都在浏览器中完成,最后资源服务器将令牌放在Fragment中,浏览器从中将令牌提取,发送给客户端。

所以这个令牌对访问者时可见的,而且客户端不需要认证。详细流程如下。

+----------+
| Resource |
|  Owner   |
|          |
+----------+
     ^
     |
    (B)
+----|-----+          Client Identifier     +---------------+
|         -+----(A)-- & Redirection URI --->|               |
|  User-   |                                | Authorization |
|  Agent  -|----(B)-- User authenticates -->|     Server    |
|          |                                |               |
|          |<---(C)--- Redirection URI ----<|               |
|          |          with Access Token     +---------------+
|          |            in Fragment
|          |                                +---------------+
|          |----(D)--- Redirection URI ---->|   Web-Hosted  |
|          |          without Fragment      |     Client    |
|          |                                |    Resource   |
|     (F)  |<---(E)------- Script ---------<|               |
|          |                                +---------------+
+-|--------+
  |    |
 (A)  (G) Access Token
  |    |
  ^    v
+---------+
|         |
|  Client |
|         |
+---------+
  • (A) 客户端将用户导向认证服务器, 携带客户端ID及重定向URI;
  • (B) 用户是否授权;
  • (C) 用户同意授权后,认证服务器重定向到A中指定的URI,并且在URI的Fragment中包含了访问令牌;
  • (D) 浏览器向资源服务器发出请求,该请求中不包含C中的Fragment值;
  • (E) 资源服务器返回一个网页,其中包含了可以提取C中Fragment里面访问令牌的脚本;
  • (F) 浏览器执行E中获得的脚本,提取令牌;
  • (G) 浏览器将令牌发送给客户端。

1)在A步骤中,客户端发送请求,需要包含这些参数:

参数名称 参数含义 是否必须
response_type 授权类型,此处值为token 必须
client_id 客户端的ID。 必须
redirect_uri 重定向的URI。 可选
scope 权限范围。 可选
state 客户端的当前状态。指定后服务器会原封不动返回。 推荐

如:

GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
    &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
  1. 在C中,认证服务器返回的URI中,参数主要有:
参数名称 参数含义 是否必须
access_token 访问令牌。 必须
token_type 令牌类型。 必须
expires_in 过期时间。 推荐
scope 权限范围。 可选
state 客户端访问时如果指定了,原封不动返回。 可选

如:

HTTP/1.1 302 Found
Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
          &state=xyz&token_type=example&expires_in=3600

我们可以看到C中返回的是一个重定向,而重定向的这个网址的Fragment部分包含了令牌。

D步骤中就是访问这个重定向指定的URI,而且不带Fragment部分,服务器会返回从Fragment中提取令牌的脚本,最后浏览器运行脚本获取到令牌发送给客户端。

目录
相关文章
|
3月前
|
缓存 安全 数据库
认证服务---OAuth2.0基本介绍,微博登录整合到实际项目中【下篇】
该博客文章介绍了如何在项目中实际应用社交登录功能,通过使用微博的OAuth 2.0授权流程来实现用户的登录和注册。
认证服务---OAuth2.0基本介绍,微博登录整合到实际项目中【下篇】
|
6月前
|
存储 监控 前端开发
前端知识笔记(五)———前端密钥怎么存储,才最安全?
前端知识笔记(五)———前端密钥怎么存储,才最安全?
1358 0
|
安全 API 数据安全/隐私保护
理解OAuth 2.0并实现一个客户端 | 青训营笔记(下)
理解OAuth 2.0并实现一个客户端 | 青训营笔记(下)
105 0
|
存储 API 数据库
OAuth 2 实现单点登录,通俗易懂...(上)
OAuth 2 实现单点登录,通俗易懂...(上)
566 0
OAuth 2 实现单点登录,通俗易懂...(上)
|
缓存 JSON NoSQL
服务项目:尚融宝(22)(后端搭建:上手访问令牌)
分布式,SSO(single sign on)模式:单点登录英文全称Single Sign On,简称就是SSO。它的解释是:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。
服务项目:尚融宝(22)(后端搭建:上手访问令牌)
|
安全 Java API
OAuth 2 实现单点登录,通俗易懂...(下)
OAuth 2 实现单点登录,通俗易懂...(下)
258 0
OAuth 2 实现单点登录,通俗易懂...(下)
|
前端开发
前端工作总结228-第三方登录请求参考
前端工作总结228-第三方登录请求参考
105 0
|
数据安全/隐私保护
FastAPI 学习之路(二十八)使用密码和 Bearer 的简单 OAuth2
FastAPI 学习之路(二十八)使用密码和 Bearer 的简单 OAuth2
FastAPI 学习之路(二十八)使用密码和 Bearer 的简单 OAuth2
|
安全 Java 数据安全/隐私保护
实战篇:Security+JWT组合拳 | 附源码
实战篇:Security+JWT组合拳 | 附源码
143 0
实战篇:Security+JWT组合拳 | 附源码
|
Web App开发 算法 Java
码农翻身讲网络4:从Token到Sessions说到OAuth认证和CAS单点登录
干掉状态:从session到token   原创: 刘欣 码农翻身 2017-03-20 1 美好的旧时光 我经常怀念三十年前那美好的旧时光, 工作很轻松, 生活很悠闲。