一、Authorization & OAuth 2
授权概念来自于物理世界也广泛存在于信息世界,授权来自于权限,关乎隐私与安全。
举三个例子:
- 对于写字楼或者小区的访客管理,访客在得到业主的授权后,经过岗亭保安验证就可以放行
- 本地生活应用当前使用最广泛的快递上门取件场景中,快递员可以通过获取取件码关联上寄件人与寄件物品
- APP 投放广告拉新下载的小游戏,在得到用户的同意后,不需要账号密码就可以使用原 APP 的用户信息直接登录到游戏中
这三个例子覆盖了纯线下、线上线下结合、纯线上的三种场景,当然随着数字化的深入纯线下的也在向线上迁移。他们都涉及到了权限的转移与使用,也就是授权(Authorization),比如进入写字楼的权限、寄件的权限、登录 APP 的权限等。
由此可见,授权有着大量应用场景,已然成为信息化基础设施的重要组成部分,OAuth 应运而生。
1、OAuth 2 起源
我们现在所熟知的 OAuth 2 就是一种授权协议,由 IETF OAuth 工作组制定的行业标准。我们常说的 OAuth 2 实际上指的是 OAuth 2.0,2.0 版本在 2012 年发布的,通过多种许可类型解决了 1.0 大一统设计却无法满足各种应用场景的问题,是事实上的行业标准。文档内容可参见:datatracker.ietf.org/group/oauth… 和 oauth.net/2/。
OAuth 2 提供了 4 种许可类型:授权码许可(Authorization Code)、资源拥有者凭据许可(Resource Owner Password Credentials)、隐式许可(Implicit)和客户端凭据许可(Client Credentials)。借助令牌机制保证只有获得令牌的访问拥有对应的资源权限,并有效减少了网络受攻击面,也是一种系统安全管控手段。
事实上,应用最广泛的还是授权码许可类型,在 OAuth 2.1 设计中,Password 与 Implicit 类型都将被移除掉。后面也将重点介绍授权码类型的原理与使用。
2、Authentication & Authorization
在一个涉及权限的软件系统中,授权对于最终使用用户而言是透明的,但对于软件设计者而言却不能混为一谈,也就是用户认证(Authentication)与授权管理(Authorization)。
认证发生是在用户访问系统过程中,用于鉴定这是谁、是否可以访问系统。 常用的认证方式有用户名密码、短信验证码、指纹识别、面部识别、U 盘认证等,单点登录(SSO)作用的也是这一阶段。信息传递通过 ID Token,是可以被用户通过登入登出所控制的。
认证过程还会关联用户账户管理(Accounting) ,也就是用户基本信息、登录状态、登录历史等。账户还会关联用户角色(Role) ,也就是用户具备什么访问权限,一般是预置的,授权过程获取用户所属角色,从而控制可访问资源。
授权发生在用户认证之后,用于鉴定用户拥有什么权限、可以访问哪些资源。 OAuth 2,JWT 是常用的技术方案。信息传递通过 Access Token。
认证、账户、角色、授权,串起了基本的用户登录模块。比如一个用户通过指纹访问 APP,系统从账户中比对指纹信息,验证通过后再获取角色信息,根据角色分配的权限拿到所属资源,从而完成登录继续使用系统设定的资源。
二、OAuth 2 简介
OAuth 的诞生,就是为了解决传统的 Client-Server 模式下受限制访问资源对三方应用开放的问题,账号密码过多暴露的安全隐患最为迫切。 另外,OAuth 来自于 Web 应用,也是基于 HTTP 设计,也为它的广泛传播提供基础。
OAuth 提供的解决思路就是增加一个授权层,引入授权服务,将三方应用客户端与资源拥有者分离开。借助授权令牌实现访问控制,资源拥有者的凭据仍然在自己手上,通过令牌施加授权,比如作用域、刷新机制、失效机制等。
OAuth 2 的广泛应用在于可扩展性,将复杂度从客户端转移到授权服务与资源服务上。也要注意它的使用条件,它是基于 Http 设计的,协议本身是授权协议而不是身份认证协议,针对用户对软件授权而没有定义用户对用户的授权机制,没有定义授权处理机制、令牌格式与加密方法等,也不是单体协议而是一个协议簇。
1、OAuth 2 的四种角色
OAuth 引入了四种角色,分别为资源拥有者(Resource Owner),受保护资源(Resource Server),客户端(Client)和授权服务(Authorization Server)。
资源拥有者是对受保护资源(Protected Resource)授予访问权限的实体,一般是指终端用户;资源服务托管受保护资源,响应使用授权令牌(Access Token)的资源访问请求。这两者不做区分的话可以直接用受保护资源来简化表示。
客户端是代表资源拥有者并获得其授权发起受保护资源申请的应用程序,可以是浏览器、也可以是后台服务等。 授权服务则是在验证资源拥有者并获取授权后向客户端颁发授权令牌。
2、Abstract Protocol Flow
抽象协议流程来自 RFC 6749。
核心流程很简洁:客户端从资源拥有者许可申请授权,再去授权服务获得授权令牌,最后从资源服务那里获得受保护资源。
借助授权令牌,实现了客户端与受保护资源的解耦,通过授权服务管理授权令牌的发放,减少密码等用户凭证的传播使用泛滥,给与受保护资源安全防护。
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
3、关键组件
除了角色外,OAuth 2 还引入了一些关键组件共同构成协议,他们有(访问)令牌(Access Token)、刷新令牌(Refresh Token)、权限范围(Scope)与授权许可类型(Grant Type)。
令牌是 OAuth 2 的一种授权机制,通过令牌代表客户端拥有访问受保护资源的权限,授权服务和资源服务需要理解令牌的含义,但是对客户端来说并不需要。OAuth 2 本身没有对令牌的格式和内容做限制,令牌的实现与部署就变得灵活。
刷新令牌考虑了用户不在场而令牌失效的应用场景,可以在不需要用户参与的情况下请求新的令牌,权衡了令牌一直有效或失效的访问控制。
权限范围表示一组访问受保护资源的权限,界定了客户端获取的权限范围,一般需要受保护资源根据自身可提供的 API 定义。
授权许可类型指的是获取令牌的方式,这里我们以设计最完备、应用最广泛的授权码类型作为主要类型举例。
4、角色与组件的交互信道与端点
端点表示一种约定好的由各个角色提供的 API,访问固定端点串起了整个流程,角色与组件之间的交互最终都落在端点上。
交互分为前端信道交互与后端信道交互,后端信道直接在服务之间借助 Http 完成,前端信道则是借助浏览器重定向实现跨域访问。
三、OAuth 2 学习路径
作为工程师或者说架构师,独立思考能力很关键,而思考的基础就是定义。
OAuth 2 来自于 IETF OAuth 工作组制定的标准协议,它本身并不是单体协议,而是以颁发令牌与使用令牌为核心,大量衍生协议做扩展的协议族,就像 HTTP 一样。
而我们常说的 OAuth 2 其实指的是这两个核心协议,即 2012 年发布的获取令牌的协议 RFC 6749,以及伴随规范定义的 bearer 令牌协议 RFC 6750,构成了 OAuth 2.0 的核心。
另一方面,从使用者角度来看,学习 OAuth 2 的设计原理与最佳实践推荐 Justin Richer 的《OAuth 2 in Action》,作者本身就是该协议的设计者。本书在 2019 年也发布了中文版《OAuth 2 实战》。
使用层面上,对于 Java 开发者,Spring 提供了两个框架支持,分别为 Spring Security 和 Spring Authorization Server,Spring Security OAuth 已经被废弃。
Spring Authorization Server 是一个提供 OAuth 2.1 和 OpenID Connect 1.0 规范及其他相关规范实现的框架。它构建于 Spring Security 之上,为构建 OpenID Connect 1.0 身份提供程序和 OAuth2 授权服务器产品提供了一个安全、轻量级和可定制的基础。
实际上 OAuth 2.1 尚未正式发布,但是经过十余年的业界实践,相对 OAuth 2.0 提供了不少改进与整合,比如:要求 PKCE,用处较少的两种授权许可类型 Resource Owner Password Credentials 和 Implicit 被舍弃,更严格要求匹配重定向 URI 等。