简介
OAuth(Open Authorization)是一个关于授权(authorization)的开放网络标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容。OAuth在全世界得到广泛应用,目前的版本是2.0版。
场景举例
让朋友去自己家取东西,如果我直接给他门禁系统的密码,朋友就拥有了与我同样的权限,这样不太合适。如果我想取消朋友进入我家的权限,需要更改密码才可以,操作麻烦。能不能有种方法,朋友可以进入我家,但是又不知道我家门禁的密码。朋友如果多次进入我家,每次都需要经过我的授权,我不授权的就进不去,我授权后才能进去。
我们设计如下方案:
第一步,在门禁系统的密码输入器下面,增加一个按钮,比如叫做"获取授权"。朋友需要首先按这个按钮,去申请授权。
第二步,朋友按下按钮以后,我的手机就会弹出提示:有人正在请求授权,并显示朋友的姓名。我确认情况属实,点击同意授权。
第三步,门禁系统得到我的确认后,向朋友发送一个进入房屋的令牌(access token),令牌是类似密码的一串数字,只在短期内有效。
第四步,朋友向门禁系统输入令牌,进入房屋。
在 OAuth2.0 中,有如下角色:
① Authorization Server:认证服务器,用于认证用户。如果客户端认证通过,则发放访问资源服务器的令牌。
② Resource Server:资源服务器,拥有受保护资源。如果请求包含正确的访问令牌,则可以访问资源。
与认证服务器是不同的逻辑节点,但是在物理上,双方是可以在一起的。
友情提示:提供管理后台、客户端 API 的服务,都可以认为是 Resource Server。
③ Client:客户端。它请求资源服务器时,会带上访问令牌,从而成功访问资源。
友情提示:Client 可以是浏览器、客户端,也可以是内部服务。
④ Resource Owner:资源拥有者。最终用户,他有访问资源的账号与密码。
友情提示:可以简单把 Resource Owner 理解成人,他在使用 Client 访问资源。
OAuth 2.0 规定了四种获得令牌的流程。
- 授权码模式(authorization-code)
- 简化模式(implicit)
- 密码模式(password)
- 客户端模式(client credentials)
>>> 可结合代码学习,代码在最下面 <<<
1. 授权码模式(authorization-code)
第三方应用先申请一个授权码,然后再用该码获取令牌。
(1) A 网站提供一个链接,用户点击后就会跳转到 B 网站,授权用户数据给 A 网站使用。
例如:
-
https://b.com/oauth/authorize? response_type=code& client_id=CLIENT_ID& redirect_uri=CALLBACK_URL& scope=read
- response_type:表示要求返回授权码;
- client_id:让B知道是谁在请求;
- redirect_uri:B接受或拒绝请求后的重定向网址;
- scope:表示要求的授权范围
(2) 用户跳转后,B 网站会要求用户登录,然后询问是否同意给予 A 网站授权。用户表示同意,这时 B 网站就会跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码。
例如:
https://a.com/callback?code=AUTHORIZATION_CODE
- code即为授权码
(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
- client_id和client_secret参数用来让 B 确认 A 的身份;
- grant_type:authorization_code,表示采用的授权方式是授权码;
- code:上一步的授权码;
- redirect_uri:令牌颁发后的回调网址
(4) B 网站收到请求以后,就会颁发令牌。返回Json数据。
例如:
{
"access_token":"ACCESS_TOKEN",
"token_type":"bearer",
"expires_in":2592000,
"refresh_token":"REFRESH_TOKEN",
"scope":"read",
"uid":100101,
"info":{...}
}
至此token(令牌)就拿到了,再请求资源时携带token即可。
一般access_token的有效期不会太长,过期前可通过refresh_token进行刷新access_token,重新颁发access_token,以此达到token续期。
2. 简化模式(implicit)
不通过第三方应用程序的服务器,直接在浏览器中向授权服务器申请令牌,跳过了“授权码”这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要授权。
(A)用户访问客户端,后者将前者跳转到到授权服务器。
(B)用户选择是否给予客户端授权。
(C)假设用户给予授权,授权服务器将用户导向客户端指定的"重定向URI",并在 URI 的 Hash 部分包含了访问令牌。
(D)浏览器向资源服务器发出请求,其中不包括上一步收到的 Hash 值。
(E)资源服务器返回一个网页,其中包含的代码可以获取 Hash 值中的令牌。
(F)浏览器执行上一步获得的脚本,提取出令牌。
(G)浏览器将令牌发给客户端。
3. 密码模式(password)
密码模式,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向授权服务器索要授权。
(A)用户向客户端提供用户名和密码。
(B)客户端将用户名和密码发给授权服务器,向后者请求令牌。
(C)授权服务器确认无误后,向客户端提供访问令牌。
第一步,A 网站要求用户提供 B 网站的用户名和密码。拿到以后,A 就直接向 B 请求令牌。
例如:
https://oauth.b.com/token?
grant_type=password&
username=USERNAME&
password=PASSWORD&
client_id=CLIENT_ID
- grant_type:password;
- username和password是 B 的用户名和密码;
- client_id参数让 B 知道是谁在请求
4. 客户端模式(client credentials)
客户端模式,指客户端以自己的名义,而不是以用户的名义,向授权服务器进行认证。
严格地说,客户端模式并不属于 OAuth 框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求授权服务器提供服务,其实不存在授权问题。
(A)客户端向授权服务器进行身份认证,并要求一个访问令牌。
(B)授权服务器确认无误后,向客户端提供访问令牌。