1. OAuth2解决什么问题
情景:
小妹在QQ空间积攒了多年的照片,想挑一些照片进行打印,然后在某宝上找了一家提供在线打印网店(我们就叫它WD)。那么问题来了,照片怎么给?
1、自己一张张下载下来提供给WD
2、将自己的空间账号秘密给WD,然后告诉WD我要打印哪些照片
针对方案:
- 一个一个下载,有几百个,眼花了……….甩手不干
- 交出自己账号密码,还要告诉WD哪些要打印,哪些不要,但是自己有些照片不想让WD看到…..
然后正义的化身OAuth2来了,小手挥一挥,立马解决了小妹的烦恼,预知怎么解决,请看下文↓
2. OAuth2简介
总的来说,OAuth2是一个开放授权的标准,该标准允许用户(小妹)让第三方应用(WD)访问该用户在某服务的特定私密资源(空间中小妹需要打印的照片),而在无需提供用户名和密码,且采取灵活设置令牌(Access Token)有效期可以让用户(小妹)对第三方应用(WD)授权或者回收权限。
OAuth2是OAuth协议的下一个版本,但是不向下兼容OAuth1.0,因为传统Web开发登录认证一般都是基于Session,但前后端分离架构或者跨平台(Android、IOS、微信小程序)不支持Cookie,使用极其不方便,应用不广,所以使用OAuth2授权能解决。
2.1 OAuth2的四个重要角色
OAuth2的完整授权流程中有四个重要角色参与进来:
1.Resource Owner:资源拥护者,上面例子的小妹
2. Resource Server:资源服务器,上面例子的QQ空间,它是小妹想要分享照片给WD的提供方
3. Client:第三方应用客户端,上面例子的WD,代指仁和可以消费资源服务器的第三方应用
4. Authorization Server:授权服务器,管理Resource Owner、Resource Server、Client三角关系的中间层。
OAuth2解决问题的关键在于使用Authorization server提供一个访问凭据给Client,使得Client可以在不知道Resource owner在Resource server上用户名和密码的情况下消费Resource Owner的受保护的资源。
3. 部署OAuth2需要完成的工作
3.1 作为Resource server
在一般情况下,Resource server提供Authorization server服务,主要提供两类接口:
1.授权接口:接受Client的授权请求,引导用户到Resource server完成登录授权的过程
2. 获取访问令牌接口:使用授权接口提供的许可凭据来颁发Resource owner()的访问令牌给Client,或者由Client更新过期的访问令牌
除此之外,还需要提供一个第三方应用程序注册管理的服务。通常情况下会为注册完成的第三方应用程序分配两个成对出现的重要参数:
- client_id:第三方应用程序的一个表示id,通常是公开的信息,用来区分哪一个第三方应用程序
- client_secret:第三方应用的私钥信息,这是私密信息,不允许在OAuth2流程中传递,用于安全方面的检测和加密
3.1 作为Client
在Client取得client_id和client_secret之后。使用这些信息来发起授权请求、获取access_token请求和消费受保护的资源。
4. OAuth2的授权流程
场景:
1、WD问QQ空间:我想要小妹的一些照片打印
2、QQ空间说:我要经过小妹同意,然后去问小妹是否授权给WD来拿照片打印
3、小妹对QQ空间说:我给WD一个临时钥匙,如果他有,你就把我照片给他
4、WD用小妹给的钥匙拿到了照片去打印
在上面流程中,①-④是授权的过程(参与者有小妹、WD、QQ空间),⑤-⑥是资源消费的过程(参与者有WD和QQ空间)
接下来分步对上图流程解释每步做了什么
1、WD向小妹要了个授权码,好去和管理钥匙的人(授权服务器)去要访问令牌
2、小妹同意并给了他授权码
3、WD拿着授权码到授权服务器那里去换取令牌(Access_Token)
4、授权服务器验证了授权码,就把访问令牌给了WD
5、WD又拿着访问令牌去请求放在QQ空间里面的照片
6、QQ空间根据访问令牌,返回了小妹的照片给WD
其中很重要的一个概念就是访问令牌,它代表的信息是整个OAuth2的核心,也是①-④步骤最终要得到的信息。
访问令牌表示的是网店可以在QQ空间里访问小妹特指的照片,如果要访问其他照片,那就是另一个访问令牌了。
访问令牌有几个重要信息:
1、客户端标识(比如网店WD)
2、用户标识(比如小妹)
3、客户端能访问资源所有者的哪些资源以及相对应的权限
有了这三个信息,资源服务器就可以区分是哪个第三方应用有权限访问哪个用户的哪些资源
5. OAuth2的4种授权模式
OAuth2一共有4种授权模式,上面小妹、WD、QQ空间用的就是其中一种授权码,也是目前应用中比较常用的模式。
- 授权码模式
- 简化模式
- 密码模式
- 客户端模式
5.1 授权码模式
这是OAuth2最常用的一种授权模式,比如微博、豆瓣、QQ等等都在用。
具体流程:
(1)Client使用浏览器(用户代理)访问Authorization server。也就是用浏览器访问一个URL,这个URL是Authorization server提供的,访问的Client需要提供(客户端标识、请求范围、本地状态、重定向URL)这些参数。
(2)Authorization server验证Client在(1)中传递的参数信息,如果没问题提供一个页面供Resource owner登陆,登陆成功后选择Client可以访问Resource server的哪些资源以及读写权限。
(3)在(2)步骤没问题后返回一个授权码(Authorization Code)给Client。
(4)Client拿着上(3)步骤获得的授权码(Authorization Code)和(客户端标识、重定向URL等信息)作为参数,请求Authorization server提供的获取访问令牌的URL。
(5)Authorization server返回访问令牌和可选的刷新令牌以及令牌有效时间等信息给Client。
5.1.1 授权请求
对应(1),客户端提供以下参数请求Authorization server:
1 | response_type:(必选)值固定为“code” |
请求示例如下:
1 | GET /authorize?response_type=code&app_id=wx201800a28199&state=wilson&redirect_uri=https://www.aaa.com&scope=user,photo HTTP/1.1 |
5.1.2 授权请求响应
对应步骤(3)Authorization Server会返回如下信息:
1 | code:授权码 |
响应示例如下:
1 | HTTP/1.1 302 Found |
Location头部信息是步骤(1)提供的redirect_uri地址,同时携带code信息和state信息给client,这样浏览器在重定向的时候就会以GET的方式访问Client提供的redirect_uri,同时Client接收到code信息和state信息。下一步就可以请求access_token了。
5.1.3 访问令牌请求
对应步骤(4),客户端提供以下参数请求Authorization Server:
1 | grant_type:(必传)固定值为"authorization_code" |
请求示例如下:
1 | POST /token HTTP/1.1 |
5.1.4 访问令牌请求响应
对应步骤(4),Authorization Server会返回如下典型的信息:
1 | access_token:访问令牌 |
示例响应如下:
1 | HTTP/1.1 200 OK |
5.2 简化模式
此种授权方式是授权码方式的简化版本,其中省略了颁发授权码code给第三方应用的步骤,通过一次请求授权服务器直接返回访问令牌以及刷新令牌等信息,适用于没有server服务器来接受处理授权码的第三方应用。简化模式的弊端很明显,因为没有后端,所以非常不安全,除非你对安全性要求不高,否则不建议使用。
5.2.1 授权请求
重点区别在于response_type为“token”,而不再是“code”,客户端提供以下参数请求Authorization Server:
1 | response_type:(必填)此处值为 "token" |
请求url示例如下:
1 | GET /authorize?response_type=token&app_id=wx201800a28199&state=wilson&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Foauth2&scope=user,photo HTTP/1.1 |
5.2.2 授权请求响应
Authorization Server会返回如下典型的信息:
1 | access_token: 访问令牌 |
返回url示例如下:
1 | HTTP/1.1 302 Found |
注意和授权码模式的最大区别在于它是把token信息放在了url的hash部分(#后面),而不是作为参数(?后面)。这样浏览器在访问重定向的Location指定的url时,就不会把这些数据发送到服务器。而Client可以通过读取Location头信息中获取到access_token信息。
5.3 密码模式
这种模式再一步简化,和授权码类型下重要的区分就是省略了授权请求和授权响应。而是Client直接使用Resource owner提供的username和password来直接请求access_token(直接发起访问令牌请求然后返回访问令牌请求响应信息)。这种模式一般适用于Resource server高度信任第三方Client的情况下。
5.3.1 访问令牌请求
客户端提供以下参数请求授权服务器:
1 | grant_type:(必填)值固定为 "password" |
请求示例如下:
1 | POST /token HTTP/1.1 |
5.3.2 访问令牌请求响应
响应示例如下:
1 | HTTP/1.1 200 OK |
5.4 客户端模式
这种类型就更简化了,Client直接已自己的名义而不是Resource owner的名义去要求访问Resource server的一些受保护资源。
5.4.1 授权请求(以自己的名义)
客户端提供以下参数请求Authorization Server:
1 | grant_type:(必填)值固定为 "client_credentials" |
请求示例如下:
1 | POST /token HTTP/1.1 |
5.4.2 授权请求响应
响应示例如下:
1 | HTTP/1.1 200 OK |
6. OAuth2刷新令牌
在得到访问令牌(access_token)时,一般会提供一个过期时间和刷新令牌。以便在访问令牌过期失效的时候可以由客户端自动获取新的访问令牌,而不是让用户再次登陆授权。
6.1.1 刷新访问令牌请求
客户端需要提供给Authorization Server的参数:
1、grant_type:必选。固定值“refresh_token”。
2、refresh_token:必选。客户端得到access_token的同时拿到的刷新令牌。
请求示例如下:
1 | POST /token HTTP/1.1 |
6.1.2 刷新访问令牌请求响应
请求响应示例如下:
1 | HTTP/1.1 200 OK |
7. Token的传递方式
主要有三种方式可作为参考:
1.URI追加access_token参数
2. 请求头放置access_token参数
3. 请求体添加access_token参数
7.1 URI追加access_token参数
在我们请求受保护的资源的Url后面追加一个access_token的参数即可。另外还有一点要求,就是Client需要设置以下Request Header的Cache-Control:no-store,用来阻止access_token不会被Web中间件给log下来,属于安全防护方面的一个考虑。
代码示例如下:
1 | GET /resource?access_token=mF_9.B5f-4.1JqM HTTP/1.1 |
7.2 URI追加access_token参数
因为在HTTP应用层协议中,专门有定义一个授权使用的Request Header,所以也可以使用这种方式
代码示例如下:
1 | GET /resource HTTP/1.1 |
其中”Bearer “是固定的在access_token前面的头部信息。
7.3 URI追加access_token参数
使用Request Body这种方式,有一个额外的要求,就是Request Header的”Content-Type”必须是固定的“application/x-www-form-urlencoded”,此外还有一个限制就是不可以使用GET访问,这个好理解,毕竟GET请求是不能携带Request Body的。
代码示例如下:
1 | POST /resource HTTP/1.1 |
8. 总结 & 参考
OAuth2是一种授权标准框架,用来解决的是第三方服务在无需用户提供账号密码的情况下访问用户的私有资源的一套流程规范。与其配套的还有其他相关的规范,都可以到https://oauth.net/2/去延伸阅读和了解。
相关参考:
https://oauth.net/2/
https://www.oauth.com/
https://www.cnblogs.com/linianhui/p/oauth2-authorization.html
http://www.javaboy.org/
http://www.ruanyifeng.com/blog/archives.html