如图所示,是密码模式的最精简架构层次,在实际开发中可以此作为基础进行扩展。密码模式涉及到五种主要角色,另外还有一个用户代理/浏览器角色:
整个流程分为两个阶段:
至此,认证授权阶段完成。其中步骤 5-6 也有其他会话方案,比如 REST 型应用可能会将 token 存储在浏览器端,但 session/cookie 方案无疑是最稳妥的选择。
在一般的 Web 应用中,可以将此阶段看作是一次用户登录过程。
至此,授权后请求资源阶段完成。
事实上 scope 参数不是核心的内容,实际工作中为了简化开发步骤甚至可以忽略它。scope 参数是用来约束客户端的权限的,跟用户权限(authorities)是不同的。比如可以在 idp 中利用 scope 参数约束某客户端只能发起读(GET)型请求,或只能调用指定的几个 API 等,具体业务逻辑自行编写。
我们仍以 PAPS 相册预览系统为例,介绍密码模式在微服务场景下的架构层次和主要流程。
微服务场景下,增加了一个网关,网关实际上是一个反向代理,将用户的请求转发到内部服务器。类似地,微服务场景下也分为两个阶段,而且第一阶段没什么变化,主要不同在于第二阶段:
此流程有两项重大变化:一是加入网关使得整个流程复杂了一些;二是网关以内使用 JWT 作为令牌。关于这两点的解读,本文不再赘述,感兴趣的同学可以参阅本人早前的文章《微服务架构下的统一身份认证和授权》。
值得注意的是,步骤 9-11,还有另一种处理方法,即将 scope 客户端权限检查放到网关进行:
客户端权限检查放到网关,则网关要维护 scope 和客户端权限的逻辑。
我们以 IBCS 图片分类系统为例,介绍客户端模式在微服务场景下的架构层次和主要流程。
可以看到,客户端模式流程比较简单,这里就不再叙述具体流程了,不过请注意第 2 步:
我们仍以 PAPS 相册预览系统为例,介绍授权码模式在微服务场景下的架构层次和主要流程。
整个流程分为两个阶段:
该阶段的流程,与密码模式的微服务场景流程一致,此处不在赘述。
如果 demo 应用是一个 REST 型应用,则在第 1、2 步骤中,还可以这么处理:
还可以这么处理:
至此,授权码模式的认证授权全流程完毕。
讨论:客户端第一次将用户导向 idp 提供的认证授权页面时,idp 是否需要验证客户端的身份呢?或者说需不需要提供 client_secret 呢?
在微服务场景下,大量的请求和响应都经过网关转发,我们设想一下,如果类似 PAPS 相册系统那样,返回的是图片数据,而且不采用 CDN 分发网络或文件存储服务等技术,那么图片流通过网关再返回给用户,网关的负载是不是将会非常庞大呢?
当然,网关本身可以做负载均衡,可以引入缓存,数据流可以做 CDN 处理等,这些都是非常好的高性能方案,除此之外,有没有其他办法呢?
这里引入一个网络技术领域的概念——负载均衡有三种模式:反向代理、透传模式和三角模式。这里简单介绍一下三角模式:
假设一种网络结构,包括 Web 服务器、PC 客户端和负载均衡器。PC 通过负载均衡器发起对 Web 服务器的访问。在三角模式下的访问流程如下:
PC、Web 服务器和负载均衡器三者呈三角形状,因此叫做三角模式。这个模式的优点就是负载均衡器只负责转发请求,而响应数据包则不需要接收和转发,从而大副降低负载均衡器自身的数据流通压力。三角模式也类似于 LVS 的 DR 模式,LVS 调度器只负责接收和转发请求,后端的集群服务器返回的真实数据包将直接发往请求的客户端。
按照这个思路,我们可以将三角模式引入网关,从而大副降低网关的负载压力。
我们设想一个场景,团队研发的平台同时包含了 IBCS 图片分类服务和 PAPS 相册预览服务,那么用户在登录平台(用密码模式认证授权)后,先访问“我的相册”,然后从中选择一张照片发起物品识别的请求。
根据文章的模式选型分析,IBCS 服务应采用客户端模式,PAPS 服务应采用密码模式,那么,客户端是否应该申请两套令牌?
回答这个问题,我们还是要从具体场景切入分析:
授权码模式是最严格的,密码模式次之,客户端模式最差,因此一般情况下,授权码模式的令牌可以给其他模式使用,密码模式令牌可以给客户端模式使用,客户端模式只能自己使用。
好了,从本节开始我们脱离枯燥的理论环节,进入一样枯燥的实战开发频道。
目前构建 OAuth2 授权系统有三种方式:一是基于主流的开源组件构建;二是接入第三方授权服务(如 Google、GitHub OAuth2);三是根据 OAuth2 的标准规范自行开发相关授权组件。
常用的开源组件有 RedHat Keycloak、Spring Security、Spring Security OAuth2,以及刚起步的 Spring Authorization Server 等。值得一提的是 RedHat Keycloak,它是一款开源、成熟的 IAM 解决方案,功能强大且可私有化部署。
在 Spring 的开发生态里,建议采用 Spring Security 方向的开源组件方案,可扩展性好,定制性强,用户广泛且社区活跃。
Spring Security OAuth2 目前已停止更新,官方不推荐继续使用。相关功能已经迁移到 Spring Security,但授权服务器(Authorization Server)功能并未包含。Authorization Server 功能将由 Spring Security 团队主导开发的 Spring Authorization Server 开源组件提供,详细信息请查看官方通告:https://spring.io/blog/2020/04/15/announcing-the-spring-authorization-server
Spring Authorization Server 项目目前还在迭代中,该项目的开发计划托管在 ZenHub 上,感兴趣的同学可以自行了解: https://app.zenhub.com/workspaces/authorization-server-5e8f3182b5e8f5841bfc4902/board?repos=248032165
核心组件,当前几乎所有 Spring OAuth2 开源技术方案都依赖于它。核心模块: spring-security-core。
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.5.1</version>
</dependency>
</dependencies>
依赖于 spring-security。该组件现已合并到 spring-security 中,官方已不建议使用。在此之前是 Spring Security 团队官方维护的唯一且被广泛使用的组件。
<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.5.1.RELEASE</version>
</dependency>
依赖于 spring-security。在 Spring Security 官方宣布停止 spring-security-oauth2 组件更新后,推出的授权服务器 Authorization Server 的替代组件。 spring-security 整合 spring-security-oauth2 后,并不包括 Authorization Server 功能,因此如果需要开发此功能,则要搭配 spring-security-oauth2-authorization-server 一起使用。
<!-- https://mvnrepository.com/artifact/org.springframework.security.experimental/spring-security-oauth2-authorization-server -->
<dependency>
<groupId>org.springframework.security.experimental</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.1.2</version>
</dependency>
依赖于 spring-security。相当于 spring-boot + spring-security ,默认包含 SecurityAutoConfiguration.class ,因此会执行一些自动化配置,可以简化开发步骤。如果想关闭自动配置,可以修改 Spring Boot 启动注解为 @SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
依赖于 spring-boot-starter-security + spring-security-oauth2,并额外提供了许多功能实现,在微服务场景下比较实用。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
由于 spring-security-oauth2 已经迁移到 spring-security,而 spring-boot-starter-security 集成了 spring-security,且做了许多简化配置,特别适合于构建 spring-boot 程序。截至 2021年8月,spring-authorization-server 的最新版本是 0.1.2,最新的消息请关注官方动态 https://spring.io/blog/2020/04/15/announcing-the-spring-authorization-server
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security.experimental/spring-security-oauth2-authorization-server -->
<dependency>
<groupId>org.springframework.security.experimental</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.1.2</version>
</dependency>
Spring Authorization Server 项目遵守 OAuth2.1 规范而不支持 password 模式,因此需要是用此模式的项目可以考虑备选方案。
https://app.zenhub.com/workspaces/authorization-server-5e8f3182b5e8f5841bfc4902/issues/spring-projects/spring-authorization-server/459
此方案采用了 spring-security-oauth2,已被 Sprnig Security 官方停止更新,因此不再推荐。另外请注意的 spring-security-oauth2 2.4.0.RELEASE 及之后的版本会提示 deprecated。
<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth.boot/spring-security-oauth2-autoconfigure -->
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<!-- spring-security-oauth2-autoconfigure 2.1.3.RELEASE 及之后的版本会提示 deprecated。
spring-cloud-starter-oauth2 集成了 spring-security-oauth2,且做了许多简化配置,是在很长一段时间里基于开源软件构建 OAuth2 的主要方案之一。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>