今天我将和大家一起探讨在系统安全领域非常常见的一种授权协议,这就是OAuth2协议,这个协议通常用于对请求访问进行安全控制。在引入这个协议之前,让我们先来回顾两个基本概念,一个是认证,一个是授权。这两个概念比较容易混淆,它们的英文也比较类似,分别是Authentication和Authorization。
我们首先需要明确,所谓认证,解决的是"你是谁"这一个问题,也就是说对于每一次访问请求,系统都能判断出访问者是否具有合法的身份标识。
一旦明确 "你是谁"之后,下一步就可以判断"你能做什么",这个步骤就是授权。通用的授权模型通常都是基于权限管理体系的,也就是说是对资源、权限、角色和用户的一种组合处理。
如果我们将认证和授权结合起来,就构成了对系统中资源进行安全性管理的一种最常见的解决方案,也就是说先判断资源访问者的有效身份,然后再来确定他是否具备对这个资源进行访问的合法权限,如下图所示:
我们今天要介绍的OAuth2协议本质上是一种授权协议,解决的是分布式环境下开放和消费第三方接口中的授权问题。为了更好的理解OAuth2协议的应用场景,我们来看一个例子。
在提倡互联网医疗的大背景下,假设说你要开发一个中医问诊平台,该平台可以根据用户存储在康美大药房(一家知名的中医在线购药平台)中的中药饮片购买记录来智能地计算用户所可能具有的中医症状,并自动推荐相关的中医专家。这样的话,用户为了使用问诊服务,就必须让该问诊平台读取自己在康美大药房上的药品购买记录。
在这个场景中,难点在于只有得到用户的授权,康美大药房才会同意问诊平台读取用户的药品购买记录。那么问题就来了,问诊平台怎么样才能获得用户的授权呢?一般想到的方法是用户将自己的康美大药房用户名和密码告诉问诊平台,然后问诊平台就可以通过用户名和密码登录到康美大药房并读取用户的购药记录,整个过程如下图所示:
上图中的方案虽然可行,但显然存在几个严重的缺点:
- 问诊平台为了开展后续的服务,会保存用户在康美大药房上的密码,这样很不安全。
- 问诊平台拥有了获取用户存储在康美大药房上所有资料的权力,用户没法限制问诊平台获得授权的范围和有效期。
- 用户只有修改密码,才能收回赋予问诊平台的权力,但是这样做会使得其他所有获得用户授权的类似问诊平台的第三方应用程序全部失效。
- 只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据产生泄漏。
既然这个方案有这么多问题,那么有没有更好的办法呢?答案是肯定的,OAuth2协议的诞生就是为了解决这些问题。
针对上述场景,我们实际上已经引出了OAuth2协议中所具备的四个核心角色,如下图所示:
在上图中,我们注意到:
- OAuth 2协议中把需要访问的接口或服务统称为资源(Resource), 每个资源都有一个拥有者(Resource Owner),也就是案例中的用户。
- 案例的中医问诊平台代表的是一种第三方应用程序(Third-party Application),通常被称为客户端(Client)。客户端在获得资源所有者的授权后可以去访问对应的资源。
- 康美大药房在案例中的角色是服务提供商,拥有一个资源服务器(Resource Server)和一个授权服务器(Authorization Server)。其中资源服务器存放着用户资源,案例中的用户购药记录就是一种用户资源。而授权服务器则对资源拥有者的身份进行认证,完成授权审批流程,并最终颁发一个访问令牌(Access Token)
进到这里,你可能会问,所谓的访问令牌是什么?令牌是OAuth2协议中非常重要的一个概念,本质上也是一种代表用户身份的授权凭证,但与普通的用户名和密码信息不同,令牌具有针对资源的访问权限范围和有效期,如下所示就是常见的一种令牌信息:
{
"access_token": "b7c2c7e0-0223-40e2-911d-eff82d125b80",
"token_type": "bearer",
"refresh_token": "40ee99d5-90f6-43ce-920f-383a619fc806",
"expires_in": 43199,
"scope": "webclient"
}
- 上述信息中的access_token 就是OAuth2的令牌,当访问每个受保护的资源时,用户都需要携带这个令牌以便进行验证
- 针对token_type,OAuth2协议中有很多中可选的令牌类型,包括bearer类型、mac类型等,这里指定的是最常见的一种类型,就是bearer类型;
- expires_in属性用于指定access_token的有效时间,当超过这个有效时间的时候,access_token将会自动失效
- refresh_token的作用在于,当access_token 过期之后,重新下发一个新的access_token;
- scope指定了可访问的权限范围,这里指定的是访问Web资源的"webclient"
介绍完令牌之后,你可能会好奇这样一个令牌究竟有什么用?接下来,就让我们来看一下如何基于令牌来完成基于OAuth2协议的授权工作流程,整个流程如下图所示:
上图流程中包含了以下6个核心步骤:
- 客户端请求用户的授权,请求中一般包含资源的访问路径、对资源的操作类型等信息
- 用户同意给予客户端授权,并将这个授权发送给客户端
- 客户端向授权服务器请求访问令牌。此时,客户端需要向授权服务器提供上一步获取的授权信息,以及客户端自身的有效身份凭证
- 授权服务器验证通过后,向客户端返回访问令牌
- 客户端携带访问令牌访问资源服务器上的资源。在令牌的有效期内,客户端可以多次携带令牌去访问资源。
- 资源服务器验证令牌的有效性以及是否过期,验证通过后才能开放服务。
在整个工作流程中,最为关键的是第二步,只有获取了这个授权之后,客户端才可以获取令牌,进而凭令牌获取资源。那么用户如何才能获取客户端授权呢?在OAuth 2.0中,定义了四种授权方式,即授权码模式(Authorization Code)、简化模式(Implicit)、密码模式(Password Credentials)和客户端模式(Client Credentials):
这里我们先来介绍最常用的授权码模式。授权码模式功能最完整,流程也最严密,该模式的特点就是通过客户端系统的后台服务器,与服务提供商的授权服务器进行交互,如下图所示:
上图中的的几个步骤说明如下:
- 用户访问客户端,客户端会将用户导向授权服务器。
- 用户选择是否给予客户端授权。
- 假设用户给予授权,授权服务器将用户导回客户端事先指定的回调地址,同时附上一个授权码。
- 客户端收到授权码,并附上回调地址,向授权服务器申请令牌。这一步是在客户端系统的后台服务上完成的,对用户不可见。
- 授权服务器核对授权码和回调地址,确认无误后,向客户端发送访问令牌。
授权码模式的特点在于当用户同意授权后,授权服务器不是马上返回最终的令牌,而是一个授权码,需要客户端携带授权码去换令牌,这就需要客户端自身具备与授权服务器进行直接交互的后台服务。
讲完授权码模式,我们再来看另一种比较常用的密码模式。相较授权码模式,密码模式比较简单,也更加容易理解,它的工作流程如下图所示:
在密码模式下,用户向客户端提供用户名和密码,然后客户端将用户名和密码发给授权服务器并请求令牌,授权服务器确认无误后,就会向客户端发放令牌。以目前主流的微服架构而言,当我们发起HTTP请求时,关注的是如何通过HTTP协议透明而高效的传递令牌,授权码模式下通过回调地址进行授权管理的方式就不是很实用,密码模式反而更加简洁高效。
到这里为止,我们已经把OAuth2协议的主题内容都介绍到了。你可能也已经注意到,虽然说OAuth2协议解决的是授权问题,但它也应用到了认证的概念,因为只有验证了用户的身份凭证,我们才能完成对他的授权。所以说,OAuth2是一款技术体系比较复杂的协议,综合应用了信息摘要、签名认证等安全性手段,并需要提供令牌以及背后的公私钥管理等功能。