彻底搞懂 OAuth2.0

194 阅读7分钟

假设你要开发一个中医问诊平台,该平台可以根据用户的中药饮片购买记录来智能地计算用户可能具有的中医症状,并自动推荐相关的中医专家。这样的话,用户为了使用问诊服务,就必须让平台读取自己在药房上的药品购买记录。

那么问题就来了,问诊平台怎么样才能获得用户的授权呢?一般我们想到的是,用户将自己的药房用户名和密码告诉问诊平台,然后问诊平台就可以通过用户名和密码登录到药房并读取用户的购药记录。

图片

这个方案虽然可行,但存在几个明显的漏洞。首先,问诊平台为了开展后续的服务,会保存用户在药房上的密码,这样很不安全;其次,问诊平台拥有了获取用户存储在药房上所有资料的权力,用户没法限制问诊平台获得授权的范围和有效期;最后,用户只有修改密码,才能收回赋予问诊平台的权力,但是这样做会使得其他所有获得用户授权的类似问诊平台的第三方应用程序全部失效。而且只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据产生泄漏。

听着是不是很可怕?没事的,我们有 OAuth2.0 协议。与传统方法相比,OAuth2 协议具有一系列的优势。

首先,针对密码的安全性,在 OAuth2 协议中,密码还是由用户自己保管,避免了敏感信息的泄露;其次,OAuth2 协议中所提供的授权具有明确的应用范围和有效期,用户可以根据需要限制问诊平台所获取授权信息的作用效果;最后,用户如果对自己的密码等身份凭证信息进行了修改,那么只要通过 OAuth2 协议重新进行一次授权即可,不会影响到相关联的其他第三方应用程序。

图片

为什么 OAuth2 协议能有这些优势呢?这是因为它有着四个核心的角色,资源、客户端、授权服务器和资源服务器。

图片

OAuth2 协议中把需要访问的接口或服务统称为资源(Resource),每个资源都有一个拥有者(Resource Owner),也就是案例中的用户。客户端是中医问诊平台,也就是第三方应用程序(Third-party Application)。而药房,在案例中的角色是服务提供商,服务提供商中的资源服务器存放着用户资源,案例中的用户购药记录就是一种用户资源。而授权服务器则对资源拥有者的身份进行认证,完成授权审批流程,并最终颁发一个访问令牌(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 协议的授权工作流程。也就是:

图片

  • 客户端请求用户的授权,请求中一般包含资源的访问路径、对资源的操作类型等信息

  • 用户同意给予客户端授权,并将这个授权发送给客户端

  • 客户端向授权服务器请求访问令牌。此时,客户端需要向授权服务器提供上一步获取的授权信息,以及客户端自身的有效身份凭证

  • 授权服务器验证通过后,向客户端返回访问令牌

  • 客户端携带访问令牌访问资源服务器上的资源。在令牌的有效期内,客户端可以多次携带令牌去访问资源。

  • 资源服务器验证令牌的有效性以及是否过期,验证通过后才能开放服务。

在整个工作流程中,最为关键的是第二步,只有获取了这个授权之后,客户端才可以获取令牌,进而凭令牌获取资源。那么用户如何才能获取客户端授权呢?在 OAuth 2.0 中,定义了四种授权方式,即授权码模式(Authorization Code)、简化模式(Implicit)、密码模式(Password Credentials)和客户端模式(Client Credentials):

图片

授权码模式功能最完整,流程也最严密,当用户同意授权后,授权服务器不是马上返回最终的令牌,而是一个授权码,需要客户端携带授权码去换令牌,这就需要客户端自身具备与授权服务器进行直接交互的后台服务。

图片

  • 用户访问客户端,客户端会将用户导向授权服务器。

  • 用户选择是否给予客户端授权。

  • 假设用户给予授权,授权服务器将用户导回客户端事先指定的回调地址,同时附上一个授权码。

  • 客户端收到授权码,并附上回调地址,向授权服务器申请令牌。这一步是在客户端系统的后台服务上完成的,对用户不可见。

  • 授权服务器核对授权码和回调地址,确认无误后,向客户端发送访问令牌。

我们再来看另一种比较常用的密码模式。它比较简单,也更加容易理解。

图片

在密码模式下,用户向客户端提供用户名和密码,然后客户端将用户名和密码发给授权服务器并请求令牌,授权服务器确认无误后,就会向客户端发放令牌。就目前主流的微服架构来说,当我们发起 HTTP 请求时,关注的是如何通过 HTTP 协议透明而高效的传递令牌,授权码模式下通过回调地址进行授权管理的方式就不是很实用,密码模式反而更加简洁高效。

OAuth2 中的客户端模式和简化模式因为在日常开发过程中应用得不是很多,这里就不详细介绍了。

你可能注意到,虽然说 OAuth2 协议解决的是授权问题,但它也应用到了认证的概念,因为只有验证了用户的身份凭证,我们才能完成对他的授权。所以说,OAuth2 实际上是一款技术体系比较复杂的协议,综合应用了信息摘要、签名认证等安全性手段,并需要提供令牌以及背后的公私钥管理等功能。