Java教程

OAuth 2.0

本文主要是介绍OAuth 2.0,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

OAuth 2.0 是什么

OAuth 2.0 一词中的 “Auth” 表示 “授权”,字母 “O” 是 Open 的简称,表示 “开放” ,连在一起就表示 “开放授权”。OAuth 2.0 就是一种授权协议。保证第三方软件只有在获得授权之后,才可以进一步访问授权者的数据。

OAuth 1.0: 想用一套授权机制来应对现实中的所有场景,弊端还包括安全上的固化攻击等问题。

课程简介

第一部分是基础篇,就是你必须要掌握的 OAuth 2.0 的基础知识。在这一模块中,我会和你细致地讲解授权码许可(Authorization Code)类型的流程,包括 OAuth 2.0 内部组件之间的通信方式,以及授权服务、客户端(第三方软件)、受保护资源服务这三个组件的原理。

在此基础上,我还会为你讲解其他三种常见许可类型,分别是资源拥有者凭据许可(Resource Owner Password Credentials)、隐式许可(Implicit)、客户端凭据许可(Client Credentials)的原理,以及如何选择适合自己实际场景的授权类型。这样一来,你就能掌握整个 OAuth 2.0 中所有许可类型的运转机制了,并且能够从容地在实际工作环境中使用它们。

第二部分进阶篇的内容,我会侧重讲一些 OAuth 2.0 “更高级” 的用法,可以让你知道如何更安全地用、扩展地用 OAuth 2.0。

所以,这部分内容会包括如何在移动 App 中使用 OAuth 2.0,因使用不当而导致的 OAuth 2.0 安全漏洞有哪些,以及如何利用 OAuth 2.0 实现一个 OpenID Connect 用户身份认证协议。此外,我还邀请了微服务技术领域的专家杨波老师,给我们分享了一个架构案例,基于 OAuth 2.0/JWT 的微服务参考架构。

知识体系

基础篇

OAuth 2.0之授权码许可类型

  1. 互联网中所有的受保护资源,几乎都是以 Web API 的形式来提供访问的,比如极客时间 App 要获取用户的头像、昵称,小兔软件要获取用户的店铺订单,我们说 OAuth 2.0 与安全相关,是用来保护 Web API 的。另外,第三方软件通过 OAuth 2.0 取得访问权限之后,用户便把这些权限委托给了第三方软件,我们说 OAuth 2.0 是一种委托协议,也没问题。
  2. 也正因为像小兔这样的第三方软件,每次都是用访问令牌而不是用户名和密码来请求用户的数据,才大大减少了安全风险上的“攻击面”。不然,我们试想一下,每次都带着用户名和密码来访问数量众多的 Web API ,是不是增加了这个“攻击面”。因此,我们说 OAuth 2.0 的核心,就是颁发和使用访问令牌它的本质思想是【用token来代替用户名和密码】

授权码的作用

在 OAuth 2.0 的体系里面有 4 种角色,按照官方的称呼它们分别是资源拥有者、客户端(即第三方软件)、授权服务和受保护资源

还是拿小兔软件来举例子,将官方的称呼 “照进现实”,对应关系就是,

资源拥有者 -> 小明,

第三方软件 -> 小兔软件,

授权服务 -> 京东商家开放平台的授权服务,

受保护资源 -> 小明店铺在京东上面的订单。

间接通信和直接通信

授权码许可流程有两种通信方式。一种是前端通信,因为它通过浏览器促成了授权码的交互流程,比如京东商家开放平台的授权服务生成授权码发送到浏览器,第三方软件小兔从浏览器获取授权码。正因为获取授权码的时候小兔软件和授权服务并没有发生直接的联系,也叫做间接通信。另外一种是后端通信,在小兔软件获取到授权码之后,在后端服务直接发起换取访问令牌的请求,也叫做直接通信

使用授权码:

不使用授权码,把访问令牌发送给第三方软件小兔的后端服务:

在 OAuth 2.0 中,访问令牌被要求有极高的安全保密性,因此我们不能让它暴露在浏览器上面,只能通过第三方软件(比如小兔)的后端服务来获取和使用,以最大限度地保障访问令牌的安全性。正因为访问令牌的这种安全要求特性,当需要前端通信,比如浏览器上面的流转的时候,OAuth 2.0 才又提供了一个临时的凭证:授权码。通过授权码的方式,可以让用户在授权服务上给第三方软件授权之后,还能重新回到第三方软件的操作页面上。

两个 “一伙”

OAuth 2.0 中的 4 个角色是 “两两站队” 的:资源拥有者和第三方软件“站在一起”,因为第三方软件要代表资源拥有者去访问受保护资源;授权服务和受保护资源“站在一起”,因为授权服务负责颁发访问令牌,受保护资源负责接收并验证访问令牌。

一定要有浏览器吗?

OAuth 2.0 是一个授权理念,或者说是一种授权思维。它的授权码模式的思维可以移植到很多场景中,比如微信小程序。

根据微信官方文档描述,开发者获取用户登录态信息的过程正是一个授权码的许可流程:

  • 首先,开发者通过 wx.login(Object object) 方法获取到登录凭证 code 值,这一步的流程是在小程序内部通过调用微信提供的 SDK 实现;
  • 然后,再通过该 code 值换取用户的 session_key 等信息,也就是官方文档的 auth.code2Session 方法,同时该方法也是被强烈建议通过开发者的后端服务来调用的。

你可以看到,这个过程并没有使用到浏览器,但确实按照授权码许可的思想走了一个完整的授权码许可流程。

问题

  1. 授权码被盗取后,人家不能也模拟服务器请求获取access_token吗?

答:一方面授权码也都有有效期,另外一方面除非再盗取了第三方应用软件的app_id、secret才能成功请求资源。

  1. 如果使用HTTPS是不是可以不使用授权码?也能保证安全了

答:HTTPS 和 OAuth 是两个维度的安全,HTTPS解决的信息加密传输,OAuth 解决的是用token来代替用户名和密码传输。

授权码和访问令牌的颁发流程是怎样的

小兔要去平台那里“备案”,也就是注册。注册完后,京东商家开放平台就会给小兔软件 app_id 和 app_secret 等信息,以方便后面授权时的各种身份校验。注册的时候,第三方软件也会请求受保护资源的可访问范围。

授权平台颁发授权码 code

  1. 验证基本信息:对第三方软件小兔合法性和回调地址合法性的校验、小兔请求过来的权限范围 scope 和注册时的权限做的比对
  2. 响应一个属于授权服务器上的页面,以提示小明进行授权,小明也可以选择给小兔的权限范围,如果请求过来的权限范围大于注册时的范围,就需要作出越权提示(凡是输入性数据都会涉及到合法性检查)
  3. 处理授权请求,生成授权码 code:OAuth 2.0 规范建议授权码 code 值有效期为 10 分钟,并且一个授权码 code 只能被使用一次,授权服务还需要将生成的授权码 code 跟已经授权的权限范围 rscope 进行绑定并存储
  4. 重定向至第三方软件小兔,将该 code 值告知第三方软件

颁发访问令牌 access_token

  1. 授权平台验证第三方软件小兔是否存在(验证app_id和app_secret)
  2. 验证授权码 code 值是否合法
  3. 生成访问令牌 access_token 值,授权服务还需要将授权范围跟访问令牌 access_token 做绑定

在颁发访问令牌的同时还会颁发刷新令牌 refresh_token 值,这种机制可以在无须用户参与的情况下用于生成新的访问令牌。正如我们讲到的小明使用小兔软件的例子,当访问令牌过期的时候,刷新令牌的存在可以大大提高小明使用小兔软件的体验。

JWT结构化令牌

JSON Web Token(JWT),是一种结构化、信息化令牌,结构化可以组织用户的授权信息,信息化就是令牌本身包含了授权信息。

JWT 这种结构化体可以分为 HEADER(头部)、PAYLOAD(数据体)和 SIGNATURE(签名)三部分。经过签名之后的 JWT 的整体结构,是被句点符号分割的三段内容。例如:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJVU0VSVEVTVCIsImV4cCI6MTU4NDEwNTc5MDcwMywiaWF0IjoxNTg0MTA1OTQ4MzcyfQ.1HbleXbvJ_2SW8ry30cXOBGR9FW4oSWBd3PWaWKsEXE

解码之后的数据:

HEADER 表示装载令牌类型和算法等信息,是 JWT 的头部。其中,typ 表示第二部分 PAYLOAD 是 JWT 类型,alg 表示使用 HS256 对称签名的算法。

PAYLOAD 表示是 JWT 的数据体,代表了一组数据。其中,sub(令牌的主体,一般设为资源拥有者的唯一标识)、exp(令牌的过期时间戳)、iat(令牌颁发的时间戳)是 JWT 规范性的声明,代表的是常规性操作。PAYLOAD 表示的一组数据允许我们自定义声明。

SIGNATURE 表示对 JWT 信息的加密签名。

JWT 的核心思想,就是用计算代替存储,有些 “时间换空间” 的 “味道”。当然,这种经过计算并结构化封装的方式,也减少了“共享数据库” 因远程调用而带来的网络传输消耗,所以也有可能是节省时间的

我们使用了 JWT 令牌之后,远程的服务端上面是不存储的,因为不再有这个必要,JWT 令牌本身就包含了信息。那么,如何来控制它的有效性问题呢?

给出了两种建议,一种是建立一个秘钥管理系统,将生成秘钥的粒度缩小到用户级别,另外一种是直接将用户密码当作密钥。

使用访问令牌

官方规范给出的使用访问令牌请求的方式,有三种,分别是:

  • Form-Encoded Body Parameter(表单参数)
POST /resource HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

access_token=b1a64d5c-5e0c-4a70-9711-7af6568a61fb
  • URI Query Parameter(URI 查询参数)
GET /resource?access_token=b1a64d5c-5e0c-4a70-9711-7af6568a61fb HTTP/1.1
Host: server.example.com
  • Authorization Request Header Field(授权请求头部字段)
GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer b1a64d5c-5e0c-4a70-9711-7af6568a61fb

官方的建议是采用 Authorization 的方式来传递令牌

我建议你采用表单提交,也就是 POST 的方式来提交令牌,因为表单提交的方式在保证安全传输的同时,还不需要去额外处理 Authorization 头部信息。

使用刷新令牌

刷新令牌是一次性的,使用之后就会失效,但是它的有效期会比访问令牌要长。

方式一:将访问令牌的 expires_in 值保存下来并定时检测,如果发现 expires_in 即将过期,则需要利用 refresh_token 去重新请求授权服务,以便获取新的访问令牌。

方式二:“现场”发现,收到一个访问令牌失效的响应,此时小兔软件立即使用 refresh_token 来请求一个访问令牌

受保护资源系统,需要注意的是权限的问题,这个权限范围主要包括:

不同的权限会有不同的操作,不同的权限也会对应不同的数据,不同的用户也会对应不同的数据。

OAuth 2.0的其它授权流程

资源拥有者凭据许可

如果小兔软件是官方出品,那么可以直接使用资源拥有者凭据许可:

步骤 1:当用户访问第三方软件小兔时,会提示输入用户名和密码。索要用户名和密码,就是资源拥有者凭据许可类型的特点。

步骤 2:这里的 grant_type 的值为 password,告诉授权服务使用资源拥有者凭据许可凭据的方式去请求访问。

步骤 3:授权服务在验证用户名和密码之后,生成 access_token 的值并返回给第三方软件。

客户端凭据许可

如果没有明确的资源拥有者,换句话说就是,小兔软件访问了一个不需要用户小明授权的数据,比如获取京东 LOGO 的图片地址,那可以直接使用客户端凭据许可类型:

步骤 1:第三方软件小兔通过后端服务向授权服务发送请求,这里 grant_type 的值为 client_credentials

步骤 2:在验证 app_id 和 app_secret 的合法性之后,生成 access_token 的值并返回

隐式许可

如果小兔软件就是只嵌入到浏览器端的应用且没有服务端,那就只能选择隐式许可:

步骤 1:用户通过浏览器访问第三方软件小兔。此时,第三方软件小兔实际上是嵌入浏览器中执行的应用程序。

步骤 2:通过app_id向授权服务发送请求,response_type 的值变成了 token,是要告诉授权服务直接返回 access_token 的值

步骤 3:生成 acccess_token 的值,通过前端通信返回给第三方软件小兔

隐式许可授权流程的安全性会降低很多

进阶篇

移动App中使用OAuth 2.0

没有 Server 端的 App,使用PKCE 协议,不使用 app_secret,还要防止授权码 code 失窃

有 Server 端的 App,跟普通的 Web 应用几乎没有任何差别

OAuth 2.0的安全问题

OIDC

OIDC (OpenID Connect) 是一种用户身份认证的开放标准,OIDC= 授权协议 + 身份认证,是 OAuth 2.0 的超集。单点登录、联合登录,都遵循的是 OIDC 的标准流程。

OIDC 和 OAuth 2.0 的角色对应关系

OIDC 的三个主要角色:

  • EU(End User),代表最终用户。
  • RP(Relying Party),代表认证服务的依赖方,就是上面我提到的第三方软件
  • OP(OpenID Provider),代表提供身份认证服务方

现在很多 App 都接入了微信登录,那么微信登录就是一个大的身份认证服务(OP)。一旦我们有了微信账号,就可以登录所有接入了微信登录体系的 App(RP),这就是我们常说的联合登录

OIDC 和 OAuth 2.0 的关键区别

一个基于授权码流程的 OIDC 协议流程,跟 OAuth 2.0 中的授权码许可的流程几乎完全一致,唯一的区别就是返回access_token访问令牌时多返回了一个 ID_TOKEN,我们称之为 ID 令牌。这个令牌是身份认证的关键。

ID 令牌

D 令牌是一个 JWT 格式的令牌,可以解决身份认证后的登录态问题,是 OIDC 作为身份认证协议的关键所在。

备注:传统的登录基于Session,这里使用JWT Token方案,就不需要Session了(也不需要Cookie参与了)

用 OIDC 协议标准来实现单点登录

一个用户 G 要登录第三方软件 A,A 有三个子应用,域名分别是 a1.com、a2.com、a3.com。如果 A 想要为用户提供更流畅的登录体验,让用户 G 登录了 a1.com 之后也能顺利登录其他两个域名,就可以创建一个身份认证服务,来支持 a1.com、a2.com 和 a3.com 的登录。这就是我们说的单点登录,“一次登录,畅通所有”。

这篇关于OAuth 2.0的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!