OAUTH2
基本角色定义
- 授权服务器 - 验证身份并颁发令牌的服务器(我上面代码示例中的OAuth服务器)
- 资源服务器 - 提供API资源的服务器
- 客户端应用 - 访问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" | | } | | |
|
SSO
- 浏览器访问系统 A,A 域下无 cookie。
- 给出一个重定向到 SSO 域的地址,SSO 域下也无cookie,SSO 重定向到授权方-比如QQ。
- QQ 让请求携带 authorization_code 回调/跳转回系统 A,而不是SSO。
- SSO 与 QQ 验证 authorization_code,这样给浏览器在 SSO 域种下 cookie,而且在返回值里面附带当前用户的 uid、role 和 openid。
- 用户选择一个 uid 登录,SSO 返回一个一次性的 ticketA。
- 用户携带 ticketA 访问A,A 访问SSO,一次核销 ticketA。SSO销毁 ticketA,返回一个 ssotoken和uid信息。ticket是 sso 的前置,一次性使用,代表一个 uid。所以通常 ticket 请求参数还会带有 uid 和重定向 uri 参数,校验服务可以顺便验证 uri/uid 和 ticketA 的绑定关系。
- 系统 A 在浏览器种下cookie,cookie 里带有 SSO token。
- 浏览器再带上 cookie 访问系统A,系统 A 向 SSO 认证 SSO token。认证成功,返回uid和role等信息(其实上一步也可以确认了)。
- 用户 logout,系统A 内部清除域下 cookie,清除 SSO session,向SSO要求那边也作废 sso token 和相关映射信息。