OAuth2.0
OAuth2.0简介
OAuth 2.0 是一种授权机制,允许用户授权第三方应用访问其在不同服务提供者上存储的资源,而无需将用户名和密码提供给第三方应用。OAuth 的核心就是向第三方应用颁发令牌(token)。
OAuth2的两个主要角色:
- 客户端
- 资源所有者
OAuth2.0认证的4种授权方式:
- 授权码(authorization-code):资源所有者同意后,授权服务器会向客户端发送一个授权码。客户端拿到code后,向服务器申请Token。
- 隐藏式(mplicit Token):用户授权后直接返回token给用户代理,这种模式适合于纯前端无后台的应用。
- 资源所有者密码(password):提供资源所有者的密码,通过密码访问资源
- 客户端凭证(client credentials):只需要提供client_id和client_secret即可获取授权,不涉及到用户,一般用于后端API的相关操作。
不管哪种授权方式,第三方应用申请令牌之前,都需要到系统备案,说明自己的身份,备案是为了防止令牌被滥用,之后会拿到两个身份信息识别码:
- 客户端ID(client ID)
- 客户端密钥(client secret)
授权码流程
- A提供一个链接,用户点击跳转到B网站,向用户申请授权B网站的用户数据给A网站使用。
http
https://b.com/oauth/authorize?
response_type=code&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read- 用户授权后,B网站会跳转会A网站的地址,同时携带授权码参数给到A网站。
http
https://a.com/callback?code=AUTHORIZATION_CODE- 用户代理跳转到A网站的地址后,A网站可从跳转请求中获取到授权码。之后A网站后端携带授权码向B网站请求令牌Token。
http
https://b.com/oauth/token?
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=CALLBACK_URL- B网站接受到请求后,会先验证授权码。授权通过后就会颁发令牌。具体做法是向redirect_uri指定的网址,发送一段json数据。
json
{
"access_token":"ACCESS_TOKEN",
"token_type":"bearer",
"expires_in":2592000,
"refresh_token":"REFRESH_TOKEN",
"scope":"read",
"uid":100101,
"info":{...}
}- json数据中的access_token就是令牌。
隐藏式流程
- A提供一个链接,用户点击跳转到B网站,向用户申请授权B网站的用户数据给A网站使用。
http
https://b.com/oauth/authorize?
response_type=token&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read- 用户跳转到B网站,登录后同意授权A网站。这时B网站会跳转回
redirect_uri参数指定的地址,并把令牌信息放在url的锚点上。
http
https://a.com/callback#token=ACCESS_TOKEN放在锚点上是为了防止中间人攻击,因为浏览器跳转时,锚点信息不会转发到后台服务器,减少令牌泄露的风险。
密码式流程
- A网站要求用户提供B网站的用户名和密码,获得后,A就直接向B请求令牌。
http
https://oauth.b.com/token?
grant_type=password&
username=USERNAME&
password=PASSWORD&
client_id=CLIENT_ID- B网站验证用户身份信息后,将令牌存放在json对象中,返回给A网站。
凭证式流程
- A网站向B网站发送请求。
http
https://oauth.b.com/token?
grant_type=client_credentials&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET- B网站验证通过后,直接返回令牌。
更新令牌
在授权服务器向我们颁发令牌的时候,一次性会颁发两个令牌,一个是访问令牌,一个是刷新令牌。我们可以使用刷新令牌去获取新令牌而不是重走一次完整的授权流程。
- 向授权服务器发送请求,参数带上刷新令牌。
http
https://b.com/oauth/token?
grant_type=refresh_token&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
refresh_token=REFRESH_TOKEN- 授权服务验证通过后,返回新的令牌信息。
| 授权类型 | /oauth/authorize | /oauth/token |
|---|---|---|
| 授权码 | response_type=code | grant_type=authorization_code |
| 隐藏式 | response_type=token | - |
| 密码式 | - | grant_type=password |
| 凭证式 | - | grant_type=client_credentials |
| 更新令牌 | - | grant_type=refresh_token |
令牌的使用
获得访问令牌后,就可以向资源服务器的API发送请求,具体的做法就是在请求头添加Authorization字段,令牌作为这个字段的参数值。
shell
curl -H "Authorization: Bearer ACCESS_TOKEN" \
"https://api.b.com"Github OAuth Demo
应用信息
- application-name:my-github-oauth
- client-id:32906681e7aff79f371c
- client-secrets:d8e874279a93b26d26b436805e05032ec7049e2f
Github OAuth接口
- Github授权端点
http
GET https://github.com/login/oauth/authorize?client_id=32906681e7aff79f371c&redirect_uri=http://localhost:7101/oauth/redirect- 用户同意授权,后端服务器会将授权码通过重定向方式发送到redirect_url。
http
http://localhost:7101/oauth/redirect?code=cd1b0c54a86b54509c8d如用户不同意授权,后端服务器通过重定向方式返回访问拒绝错误。
httphttp://localhost:7101/oauth/redirect?error=access_denied&error_description=The+user+has+denied+your+application+access.&error_uri=https%3A%2F%2Fdocs.github.com%2Fapps%2Fmanaging-oauth-apps%2Ftroubleshooting-authorization-request-errors%2F%23access-denied
- 使用授权码向Github获取令牌端点发送请求
http
POST https://github.com/login/oauth/access_token?client_id=32906681e7aff79f371c&client_secret=d8e874279a93b26d26b436805e05032ec7049e2f&code=cd1b0c54a86b54509c8d
Accept: application/json之后会收到返回的令牌
json
{
"access_token": "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",
"token_type": "bearer",
"scope": ""
}- 向用户信息api发送请求,获取用户信息。
http
GET https://api.github.com/user
Authorization: Bearer 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
Accept: application/json之后会返回用户信息json
json
{
"login": "chenzhuowen",
"id": 20341696,
"node_id": "MDQ6VXNlcjIwMzQxNjk2",
"avatar_url": "https://avatars.githubusercontent.com/u/20341696?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/chenzhuowen",
"html_url": "https://github.com/chenzhuowen",
"followers_url": "https://api.github.com/users/chenzhuowen/followers",
"following_url": "https://api.github.com/users/chenzhuowen/following{/other_user}",
"gists_url": "https://api.github.com/users/chenzhuowen/gists{/gist_id}",
"starred_url": "https://api.github.com/users/chenzhuowen/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/chenzhuowen/subscriptions",
"organizations_url": "https://api.github.com/users/chenzhuowen/orgs",
"repos_url": "https://api.github.com/users/chenzhuowen/repos",
"events_url": "https://api.github.com/users/chenzhuowen/events{/privacy}",
"received_events_url": "https://api.github.com/users/chenzhuowen/received_events",
"type": "User",
"site_admin": false,
"name": "ZhuoWen Chen",
"company": null,
"blog": "",
"location": "Earth/Guangzhou/Foshan",
"email": null,
"hireable": true,
"bio": null,
"twitter_username": null,
"public_repos": 7,
"public_gists": 0,
"followers": 0,
"following": 4,
"created_at": "2016-07-07T15:51:55Z",
"updated_at": "2023-10-27T09:32:22Z"
}