OAUTH2

基本角色定义

  1. 授权服务器 - 验证身份并颁发令牌的服务器(我上面代码示例中的OAuth服务器)
  2. 资源服务器 - 提供API资源的服务器
  3. 客户端应用 - 访问API的应用,可以是:
    • 第一方应用 - 您公司自己的应用
    • 第二方应用 - 业务合作伙伴的应用
    • 第三方应用 - 外部开发者的应用

token 类型

  • access_token (默认有效期 24 小时)
  • refresh_token(默认有效期 30 自然日,每次刷新 access_token 的操作可自动刷新 refresh_token 有效期的起始计算时间)。

客户端凭证流程(适用于服务器到服务器通信)

1
2
3
4
5
6
7
8
9
10
第一方/第二方应用                    授权服务器                  资源服务器(API)
| | |
|---1. 请求访问令牌------------>| |
| (使用client_id和client_secret)| |
| | |
|<--2. 返回access_token---------| |
| | |
|---3. 使用access_token调用API------------------>| |
| | |
|<--4. 返回API响应---------------------------------| |

这种流程中:

  • 获取令牌方: 第一方或第二方应用直接获取令牌
  • 使用令牌方: 同样的应用使用令牌调用API
  • 无用户参与: 整个过程无需最终用户授权
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
客户端                                认证服务器
| |
|------- POST /token ---------------->|
| grant_type=client_credentials |
| client_id=CLIENT_ID |
| client_secret=CLIENT_SECRET |
| |
|<------ 200 OK JSON 响应 ------------|
| { |
| "access_token": "eyJhbGc...", |
| "token_type": "Bearer", |
| "expires_in": 3600 |
| } |
| |
|------- 使用令牌访问API ------------>|
| Authorization: Bearer eyJhbGc... |
| |

授权码流程(适用于有用户参与的场景)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
用户          第一方/第二方/第三方应用          授权服务器          资源服务器(API)
| | | |
|----1. 访问应用---->| | |
| | | |
|<---2. 重定向到-----| | |
| 授权服务器 | | |
| | | |
|----3. 登录并授权------------------------->| | |
| | | |
|<---4. 重定向回应用,携带授权码(code)------| | |
| | | |
|----5. 返回应用,----->| | |
| 带上授权码 | | |
| | | |
| |---6. 使用code请求令牌---->| |
| | (附带client_id/secret) | |
| | | |
| |<--7. 返回access_token-----| |
| | 和refresh_token | |
| | | |
| |---8. 使用access_token调用API--------------->| |
| | | |
| |<--9. 返回API响应--------------------------------|
| | | |

这里面有两次重定向:

第一次:重定向到授权服务器。用户是带着一个 param 去访问authserver的,在我们常见的场景里线索通跳到 sso 是第一次重定向,参数里带有 https://app.example.com/callback 就是授权完以后再回到线索通的一个 url。

  • 当用户尝试访问需要授权的功能时,应用生成一个授权请求URL
  • 这个URL包含redirect_uri参数(应用事先在授权服务器注册的URL)
  • 应用通过HTTP 302重定向或者前端跳转,将用户浏览器导向授权服务器
1
2
3
4
5
6
GET https://auth-server.com/oauth/authorize?
response_type=code&
client_id=CLIENT_ID&
redirect_uri=https://app.example.com/callback&
scope=profile email&
state=random_state_value

第二次:重定向回应用,携带授权码(code)。

  • 用户在授权服务器完成身份验证并授予权限后
  • 授权服务器使用之前请求中的redirect_uri,将用户浏览器重定向回应用
  • 重定向URL中附加授权码(code)作为查询参数
1
2
3
4
HTTP/1.1 302 Found
Location: https://app.example.com/callback?
code=AUTHORIZATION_CODE_VALUE&
state=random_state_value
  • 授权服务器只会重定向到预先注册的URI,防止授权码被重定向到恶意网站
  • 防止授权码被重定向到恶意网站
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
用户/浏览器         客户端应用            认证服务器              资源服务器
| | | |
| | | |
| |-- 重定向用户认证 ->| |
| | /authorize? | |
| | client_id=...& | |
| | redirect_uri=...& | |
| | response_type=code| |
| | | |
|<-----------------|<-------------------| |
| | | |
|--- 登录界面交互 ->| | |
| | | |
| |<-- 重定向回客户端 --| |
|<-- 重定向回 -----| /callback?code=.. | |
| 客户端应用 | | |
| | | |
| |-- POST /token ---->| |
| | grant_type= | |
| | authorization_code| |
| | &code=... | |
| | &redirect_uri=... | |
| | &client_id=... | |
| | &client_secret=...| |
| | | |
| |<-- 返回令牌 -------| |
| | access_token, | |
| | refresh_token等 | |
| | | |
| |-------------------- 使用令牌访问资源 ----->|
| | Authorization: | |
| | Bearer {token} | |
| | | |
| |<------------------- 返回受保护资源数据 ----|
| | | |

刷新令牌流程 (Refresh Token Flow)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
客户端                                认证服务器
| |
|------- POST /token ---------------->|
| grant_type=refresh_token |
| refresh_token=REFRESH_TOKEN |
| client_id=CLIENT_ID |
| client_secret=CLIENT_SECRET |
| |
|<------ 200 OK JSON 响应 ------------|
| { |
| "access_token": "NEW_TOKEN", |
| "token_type": "Bearer", |
| "expires_in": 3600, |
| "refresh_token": "NEW_REFRESH" |
| } |
| |