https://www.wuhuachuan.com/visitor/learning/article/getArticleDetail?id=e70818e5-f5f1-4640-b928-f908bece0463&utm_source=tuicool&utm_medium=referral
前言
主要想总结下工作中使用的 Kong 集成 OAuth 这项技术,所以转载这篇文章先介绍 OAuth 的一些概念。如果有时间,还是读一下 OAuth RFC 文档好: 点这里
OAuth 概念
OAuth 是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表), 而不需要将用户名和密码提供给第三方应用。 OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。 每一个令牌授权一个特定的网站在特定的时段内访问特定的资源。 这样,OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。
OAuth 运行流程
首先会有几个术语:
- Resource owner : 资源所有者,本文中称为 “用户”。
- Authorization server :认证服务器,即服务提供商专门用来处理认证的服务器。
- Resource server:资源服务器,即用户提供商存放用户生成的资源的服务器。它和认证服务器,可以是同一台服务器,也可以是不同的服务器。
总的来说, 认证服务器 和 资源服务器 都由 服务提供商 提供。流程如下图:
文字解释:
- 用户打开客户端以后,客户端要求用户给予授权。
- 用户同意给予客户端授权。
- 客户端使用上一步获得的授权,向认证服务器申请令牌。
- 认证服务器对客户端进行认证后,确认无误后,同意发放令牌。
- 客户端使用令牌,向资源服务器申请获取资源。
- 资源服务器确认令牌无误后,同意向客户端开放资源。
上面的 6 个步骤中,重点在于 2 ,即用户怎么给客户端授权,有了这个授权,客户端就可以获取令牌,进而凭借令牌获取资源。
OAuth 授权模式
OAuth2.0 定义了 四种授权模式。分别为:
- 授权码模式
- 简化模式
- 密码模式
- 客户端模式
授权码 模式
授权码模式是功能最完整,流程最严密的授权模式。它的特定就是通过客户端的后台服务器,与“服务提供商”的认证服务器进行互动。流程如下图:
解释:
首先,用户访问客户端,后者将前者导向认证服务器。如: https://www.example.com/v1/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read
- www.example.com/v1/oauth/authorize :API 授权的终端。
- client_id :应用程序的client ID,用于 API 识别应用程序。
- redirect_uri :获得授权码之后,服务提供商重定向用户代理(比如浏览器)的地址。
- response_type : 表明授权类型,默认是 code。即授权码模式。
- scope: 应用程序可以获得的授权级别,默认值为 read。
- state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值,用于抵御 CSRF 攻击。
然后,用户选择是否给予授权。如下图:
假如用户给予授权,认证服务器将用户导向客户端事先指定的 “重定向URL”,同时附上一个授权码: https://www.jianshu.com/callback?code=AUTHORIZATION_CODE
客户端收到授权码,附上之前的 “重定向URI” 向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
https://www.example.com/v1/oauth/token? client_id=CLIENT_ID& client_secret=CLIENT_SECRET& grant_type=authorization_code& code=AUTHORIZATION_CODE& redirect_url=REDIRECT_URL
URI 包括:
- www.example.com/v1/oauth/authorize :API Token的终端。
- client_id :即 app key / consumer key ,用于验证应用程序。
- client_secret:即 app secret / consumer secret 用于验证应用程序。
- grant_type :刚刚获得的授权码
- redirect_uri :重定向URI,和第一步一致。
认证服务器核对了授权码 和 重定向 URI 确认无误后,向客户端发送访问令牌和 更新令牌。
{ "access_token":"ACCESS_TOKEN", "token_type":"bearer", "expires_in":2592000, "refresh_token":"REFRESH_TOKEN", "scope:read" }
包括了:
- access_token:访问令牌
- token_type :令牌类型
- expires_in :过期时间,单位为秒
- refresh_token: 更新令牌,用来获取下一次的访问令牌。
- scope:权限范围。
Ps:这里可能会有一个疑惑,为什么要发送两次请求得到 token,为什么要有 code?
是这样子的:
- 浏览器的 redirect uri 是一个不安全信道,此方式不适合于传递敏感数据(如 access token)。 因为 uri 可能通过 HTTP referrer 被传递给其它恶意站点,也可能存在于浏览器 cacher 或 log 文件中,这就给攻击者盗取 access token 带来了很多机会。 另外,此协议也不应该假设 RO 用户代理的行为是可信赖的,因为 RO 的浏览器可能早已被攻击者植入了跨站脚本用来监听 access token。 因此,access token 通过 RO 的用户代理传递给 Client,会显著扩大 access token 被泄露的风险。 但 authorization code 可以通过 redirect uri 方式来传递,是因为 authorization code 并不像 access token 一样敏感。 即使 authorization code 被泄露,攻击者也无法直接拿到 access token,因为拿 authorization code 去交换 access token 是需要验证 Client 的真实身份。 也就是说,除了 Client 之外,其他人拿 authorization code 是没有用的。 此外,access token 应该只颁发给 Client 使用,其他任何主体(包括 RO)都不应该获取 access token。 协议的设计应能保证 Client 是唯一有能力获取 access token 的主体。 引入 authorization code 之后,便可以保证 Client 是 access token 的唯一持有人。 当然,Client 也是唯一的有义务需要保护 access_token 不被泄露。
- 引入 authorization code 还会带来如下的好处。 由于协议需要验证 Client 的身份,如果不引入 authorization code,这个 Client 的身份认证只能通过第1步的 redirect uri 来传递。 同样由于 redirect uri 是一个不安全信道,这就额外要求 Client 必须使用数字签名技术来进行身份认证,而不能用简单的密码或口令认证方式。 引入 authorization_code 之后,AS 可以直接对 Client 进行身份认证(见步骤4和5),而且可以支持任意的 Client 认证方式(比如,简单地直接将 Client 端密钥发送给 AS)。
简化模式
简化模式不经过第三方应用程序的服务器,直接在浏览器中向认证服务器 申请令牌,跳过了“授权码” 这个步骤,所以步骤都在浏览器中完成,令牌对访问者是可见的,而且客户端不需要认证。这种模式一般是用于客户端应用程序,比如手机应用,桌面客户端应用程序和运行于浏览器上的Web应用程序。授权令牌会交给用户代理,再由用户代理交给应用程序。如下图:
首先,还是用户访问客户端,客户端将用户导向认证服务器。 https://www.example.com/authorize?response_type=token&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read
然后,用户选择是否给予客户端授权:
假如用户给予授权,认证服务器将用户导向客户端事先指定的 “重定向URI” ,并且在 URI 的 hash 部分包含了访问令牌: https://www.example.com/callback#token=ACCESS_TOKEN
then 浏览器向资源服务器发出请求,其中不包括上一步收到的 hash 值。
then 资源服务器返回一个网页,其中包含的代码可以获取Hash 值中的令牌。
then 浏览器执行上一步获得的脚本,提出令牌。
finally 浏览器将令牌发送给客户端。
密码模式
即用户向客户端提供用户名密码。客户端使用这些信息,向 “服务商提供商” 索要授权。在这种模式下,客户端不得存储密码。 这通常用在用户对客户端高度可信的情况下。 一般,认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。如下图:
- 用户向客户端提供用户名,密码。
- 客户端用用户名,密码发送给认证服务器,向后者申请令牌。
- 认证服务器确认无误后,向客户端提供访问令牌。
客户端模式
客户端使用自己的名义,而不是用户的名义,向“服务提供商” 进行认证。严格来说,客户端模式并不属于OAuth 框架所需要解决的问题。在这种模式下,用户直接向客户端注册,客户端以自己的名义要求“服务提供商”提供服务,其实并不存在授权。如下图:
- 客户端向认证服务器进行身份认证,并且要求一个访问令牌。
- 认证服务器认证无误后,向客户端提供访问令牌。
对于这种方式,用在 访问一些和用户无关的Open Api,比如一些首页数据,这些数据和用户无关,但是又不想任何人都可以调用这个WebApi,那么就可以采用这种模式。
参考
写在最后
- 写出来,说出来才知道对不对,知道不对才能改正,改正了才能成长。
- 在技术方面,希望大家眼里都容不得沙子。如果有不对的地方或者需要改进的地方希望可以指出,万分感谢。