简单理解OAuth 2.0

4,519 阅读12分钟

个人博客地址:blog.sqdyy.cn

OAuth2要解决的问题

开放系统间授权问题

OAuth2最初是基于开放系统间授权问题提出的,假设现在有一个第三方应用:“云冲印服务”,可以将用户存储在Google的照片冲印出来。用户为了使用该服务,必须让“云冲印服务”读取自己储存在Google上的照片。问题是只有得到用户的授权,Google才会同意“云冲印服务”读取这些照片。那么“云冲印服务”如何获取用户的授权呢?

办法1:密码用户名复制

办法1密码用户名复制

传统的办法是,资源拥有者将自己的用户名和密码告诉第三方服务,然后第三方服务再去读取用户受保护的资源,这种做法适用于公司内部应用开发时使用,在开放系统间这么做就不太合适了,因为第三方服务可能为了后续的服务,会保存用户的密码,这样很不安全。

办法2:万能钥匙

办法2万能钥匙

另一种方法是客户应用和受保护的资源之间商定一个通用developer key,用户在受保护资源方得到一个developer key交给第三方应用,第三方应用再通过这个developer key去访问用户受保护的资源。这种方式适用客户应用和受保护资源之间存在信任关系的情况,如两方是合作商,或是同个公司不同部门之间的应用。但是对于不受信的第三方应用来说这种方法也不合适。

办法3:特殊令牌

办法3特殊令牌

第三种方法是使用一个特殊令牌,它仅仅能访问受保护的资源,这种做法相对前两种方法要靠谱的多,并且和OAuth2的做法已经比较接近了,但是如何管理令牌,颁发令牌,吊销令牌就需要一些讲究了,这些我们留到后面介绍OAuth2再来了解。

现代微服务安全问题

传统单块应用的架构中,通常我们的单块应用会部署到应用服务器做成集群,其中会有一个专门用于处理登录授权的用户数据库。当用户访问应用服务器时,会通过应用的拦截器进行拦截鉴权,如果鉴权登录成功,通常由服务器发给客户端一个会话标识Session ID,客户端将Session ID存储在Cookie中,服务器记录Session ID与经过验证的用户的对应关系。

传统单块应用架构

相对而言,传统单块应用主要是直接面向PC用户的Web应用,对于现代微服务架构而言,上面的做法就不适用了。对于现代微服务架构而言,服务之间拆分的粒度较小,需要考虑服务和服务之间的鉴权问题,另外就是应用的形态变得多种多样,例如有单页应用,无线原生APP,服务端APP。这种场景下我们通常会设计一个独立的服务,将认证和授权都做成一个AuthServer,通过Token的形式进行鉴权和授权,那么这也是OAuth2解决的一个主要问题,我们将在后面进行介绍。

现代微服务架构


OAuth的基本概念

OAuth2.0是用于REST/APIs的代理授权框架(delegated authorization framework),它基于令牌Token的授权,在无需暴露用户密码的情况下,使应用能获取对用户数据的有限访问权限。Oauth2.0能将认证和授权解耦,它是事实上的标准安全框架,支持多种应用场景,如服务端WebApp,浏览器单页应用SPA,无线/原生App,服务器对服务器之间调用等等。

OAuth2.0具有以下优势:

  • 客户端不接触用户密码,服务端更易集中保护
  • 广泛传播并被持续采用
  • 支持短寿命和封装的Token
  • 资源服务器和授权服务器解耦
  • 集中式授权,简化客户端
  • HTTP/JSON友好,易于请求和传递Token
  • 考虑多种客户端架构场景
  • 客户端可以具有不同的信任级别

OAuth2.0的不足:

  • 协议框架太宽泛,造成各种实现的兼容性和互操作性差
  • 和OAuth1.0不兼容

OAuth需要注意的地方:

  • OAuth并没有支持HTTP以外的协议
  • OAuth并不是一个认证协议
  • OAuth并没有定义授权处理机制
  • OAuth并没有定义Token类型
  • OAuth2.0并没有定义加密方法
  • OAuth2.0并不是单个协议。
  • OAuth2.0仅是授权框架,仅用于授权代理。

OAuth主要角色和术语

OAuth2主要角色

  • Client Application(客户应用):通常是一个Web或者无线应用,它需要访问用户的受保护资源。
  • Resource Server(资源服务器):是一个Web站点或者Web service API,用户的受保护数据保存在此。
  • Authorized Server(授权服务器):在客户应用成功认证并获得授权之后,向客户应用颁发访问令牌Access Token。
  • Resource Owner(资源拥有者):资源的拥有人,想要分享某些资源给第三方应用。
  • Client Credentials(客户凭证):客户的clientId和密码用于认证客户。
  • Tokens(令牌):授权服务器在接收到用户请求以后,颁发访问令牌。
  • Scopes(作用域):客户请求访问令牌时。由资源拥有者额外指定细分权限(permission)

OAuth令牌类型

令牌Token是OAuth2.0的核心概念,令牌可以类比为一把仆从钥匙(Valet Key),它给应用授权有限的访问权限,让应用能够代表用户去访问用户的数据。

  • Authorization Code Token(授权码):仅用于授权码授权类型,用于交换获取访问令牌和刷新令牌。
  • Refresh Token(刷新令牌):用于去授权服务器获取一个新的令牌。
  • Access Token(访问令牌):用于代表一个用户或服务直接去访问受保护的资源。
  • Bearer Token:不管是谁拿到Token都可以访问资源。
  • Proof of Possession(Pop) Token:可以校验client是否对Token有明确的拥有权。

OAuth2授权方式

先来看OAuth2.0的运行流程:

OAuth2.0的运行流程

  • (A)用户打开客户端以后,客户端要求用户给予授权。
  • (B)用户同意给予客户端授权。
  • (C)客户端使用上一步获得的授权,向认证服务器申请令牌。
  • (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
  • (E)客户端使用令牌,向资源服务器申请获取资源。
  • (F)资源服务器确认令牌无误,同意向客户端开放资源。

这里的B步骤是关键,即客户如何给客户的授权,有了这个授权后客户端就可以获取令牌,进而凭借令牌获取资源,OAuth2.0为我们提供了4种客户端获取授权的模式:

  • 授权码模式(Authorization Code)
  • 简化模式(implicit)
  • 密码模式(resource owner password credentials)
  • 客户端模式(client credentials)

授权码模式

授权码模式相对其他三个模式来说是功能最完整,流程最安全严谨的授权方式。它的特点是通过客户端的后台服务器与服务提供商的认证服务器进行交互:

授权码模式

它的步骤如下:

  • (A)用户访问客户端,客户端将用户导向认证服务器,需要携带客户端ID凭证和重定向URI。
  • (B)用户选择是否给予客户端授权。
  • (C)假设用户给予授权,认证服务器将用户导向事先指定的重定向URI,同时附上一个授权码。
  • (D)客户端收到授权码后,在后台服务器(对用户不可见)携带事先指定的重定向URI和授权码向认证服务器申请令牌。
  • (E)认证服务器核对授权码和重定向URI,确认无误后,向客户端颁发访问令牌(access token)和刷新令牌(refresh token)。

简化模式

简化模式不通过服务端程序来完成,比授权码模式减少了“授权码”这个步骤,直接由浏览器发送请求获取令牌,令牌对访问者是可见的,且客户端不需要认证,这种模式一般用于单页应用:

简化模式

它的步骤如下:

  • (A)用户访问客户端,客户端将用户导向认证服务器,需要携带客户端ID凭证和重定向URI。
  • (B)用户选择是否给予客户端授权。
  • (C)假设用户给予授权,认证服务器将用户导向事先指定的重定向URI,并在URI的Hash部分包含了访问令牌(Fragment)。
  • (D)浏览器向资源服务器发出请求,其中不包含事先收到的Hash部分(Fragment)。
  • (E)资源服务器返回一段脚本,其中包含的代码可以获取Hash部分中的令牌。
  • (F)浏览器执行事先获取的脚本,提取出令牌
  • (G)浏览器将令牌发送给客户端。

密码模式

密码模式中,用户向客户端提供用户名和密码,客户端使用这些信息,直接向认证服务器索要授权。这种模式违背了前面提到的微服务安全要解决的问题(不暴露用户名和密码),但是在一些用户对客户端高度信任的情况下,例如公司内部软件间的授权下,使用这种模式也是适用的:

密码模式

它的步骤如下:

  • (A)用户向客户端提供用户名和密码。
  • (B)客户端将用户名和密码发送给认证服务器,向认证服务器索要令牌。
  • (C)认证服务器确认无误后,向客户端提供访问令牌。

客户端模式

客户端模式是客户端以自己的名义去授权服务器申请授权令牌,并不是完全意义上的授权。主要应用于Docker到DokcerHub拉取镜像的这类场景:

客户端模式

它的步骤如下:

  • (A)客户端向认证服务器进行身份认证,并要求获取访问令牌。
  • (B)认证服务器确认无误后,向客户端提供访问令牌。

刷新令牌

如果用户访问的时候,客户端的"访问令牌"已经过期,则需要使用"更新令牌"申请一个新的访问令牌:

刷新令牌

它的步骤如下:

  • (A)客户端向认证服务器进行身份认证,并要求获取访问令牌。
  • (B)认证服务器确认无误后,返回访问令牌和一个刷新令牌。
  • (C)客户端通过访问令牌访问受保护资源。
  • (D)如果访问令牌未过期,则向客户端提供资源服务。
  • (E)客户端通过访问令牌访问受保护资源。
  • (F)如果访问令牌过期,受保护资源服务器返回Invalid Token Error。
  • (G)客户端得到上方的错误后,通过刷新令牌向授权服务器申请一个新的访问令牌。
  • (H)认证服务器确认无误后,返回访问令牌和一个刷新令牌。

OAuth2.0四种模式的选型

上面介绍了OAuth4种客户端授权模式,下面介绍这4种模式的技术选型,在这之前先做两个概念铺垫:

授权流程渠道(channels): 前面提到了OAuth2的四个主要角色,这四个角色之间的交互可以划分成两类渠道,凡是资源拥有者、客户应用和授权服务器之间的发送交互可以划分为 前端渠道。凡是授权服务器、客户应用和资源服务器之间发生的交互可以划分为后端渠道

OAuth2主要角色

客户应用类型:客户应用也可以划分为两类应用,第一类是公开应用,主要是指单页应用SPA或原生App应用,这类应用都是驻在用户手中的,这种应用不能将用户的凭证信息如密码驻留在上面,一般只存用户标识。第二类是私密应用,主要指Web服务端应用、服务/API(机器对机器间),这种应用是在后端运行的,整体上相对安全,可以驻留用户凭证信息。

四种OAuth2.0授权模式的特征

在选型之前我们先来汇总一下四种授权类型的特征:

授权码模式

  • 通过前端渠道客户获取授权码
  • 通过后端渠道,客户使用授权码去交换访问令牌和刷新令牌
  • 假定资源拥有者和客户在不同设备上
  • 最安全的流程,因为令牌不会传递经过user-agent

简化模式

  • 适用于公开的浏览器单页应用
  • access token直接从授权服务器返回(只有前端渠道)
  • 不支持refresh token
  • 假定资源所有者和公开应用在同一个应用上
  • 最容易受到安全攻击

用户名密码模式

  • 使用用户名密码登录的应用,例如桌面App,内部软件
  • 使用用户名/密码作为授权方式从授权服务器上获取access token
  • 一般不支持refresh token
  • 假定资源拥有者和公开用户在相同设备上

客户端模式

  • 适用于服务器间通信厂家,机密客户代表它自己或者一个用户
  • 只有后端渠道,使用客户凭证获取一个access token
  • 因为客户凭证可以使用对称或非对称加密,该方式支持共享密码或者证书

授权模式选型

综合上述,在选型时可以参考下面流程图的思路:

授权模式选型

本文参考资料: