Spring Security Oauth2源码分析

Spring Security Oauth2源码分析

  • 前言
  • 一:客户端OAuth2授权请求的入口
    • 1、DefaultOAuth2AuthorizationRequestResolver类
      • OAuth2AuthorizationRequest类
      • authorizationRequestUri 的构建机制
      • redirectUri
    • 3、OAuth2AuthorizationRequestRedirectFilter类
  • 二:OAuth2授权请求是如何构建并执行的
    • 1、sendRedirectForAuthorization方法
  • 三:OAuth2授权回调的处理机制
    • 1、OAuth2LoginAuthenticationFilter类
    • 2、AuthenticationManager 的认证过程
    • 3、OAuth2 对应的 AuthenticationProvider
    • 4、OAuth2LoginAuthenticationProvider

前言

1、用户发起了一个未经身份验证的请求。
2、Spring Security 的 DelegatingAuthenticationEntryPoint 组件检测到这个未经身份验证的请求。
3、通过一系列复杂的匹配器 DelegatingAuthenticationEntryPoint 确定这个请求需要进行身份验证。
4、DelegatingAuthenticationEntryPoint 执行了 LoginUrlAuthenticationEntryPoint来处理这个未经身份验证的请求。
5、LoginUrlAuthenticationEntryPoint将用户重定向到:
{baseUrl}/oauth2/authorization/{clientRegistrationId}

一:客户端OAuth2授权请求的入口

客户端进行第三方认证操作的起点,默认格式为
{baseUrl}/oauth2/authorization/{clientRegistrationId},其中 clientRegistrationId 代表着一个第三方标识。是在授权服务器中注册的应用 唯一标识id。

用户点击了这个请求后就开始了授权之旅。Spring Security 一定是拦截到了/oauth2/authorization后才启用了 OAuth2 相关的处理逻辑。那就去抓住这个源头!

1、DefaultOAuth2AuthorizationRequestResolver类

从名称上看着是一个默认 OAuth2 授权请求解析器。它实现了接口OAuth2AuthorizationRequestResolver

public interface OAuth2AuthorizationRequestResolver {
    /**
    * 从HttpServletRequest对象中解析封装 OAuth2AuthorizationRequest
    */
	OAuth2AuthorizationRequest resolve(HttpServletRequest request);

    /**
     * 从HttpServletRequest对象以及clientRegistrationId中解析封装 OAuth2AuthorizationRequest
     */
	OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId);
}

也就是说当我们请求 /oauth2/authorization 时,DefaultOAuth2AuthorizationRequestResolver 会从/oauth2/authorization对应的HttpServletRequest中提取数据封装到 OAuth2AuthorizationRequest 请求对象中做进一步使用。

其核心方法在 DefaultOAuth2AuthorizationRequestResolver 有两个重载,这里分析一个就够了

@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
    // registrationId是通过uri路径参数/oauth2/authorization/{registrationId}获得的
    String registrationId = resolveRegistrationId(request);
	if (registrationId == null) {
	    return null;
	}
	// 然后去请求对象request中提取key为action的参数,默认值是login
	String redirectUriAction = getAction(request, "login");
	// 然后进入根本的解析方法
	return resolve(request, registrationId, redirectUriAction);
}

OAuth2AuthorizationRequest类

resolve(request,registrationId,redirectUriAction)方法才是最终从 /oauth2/authorization 提取 OAuth2AuthorizationRequest 的根本方法。resolve方法会根据不同的授权方式 (AuthorizationGrantType) 来组装不同的OAuth2AuthorizationRequest对象。

AuthorizationGrantType ,来自配置:

spring.security.client.registration.{registrationId}.authorization-grant-type= ××××××

OAuth2AuthorizationRequest 的几个参数的规则是固定的:

1、clientId 来自于配置,是第三方平台给予我们的唯一标识。
2、authorizationUri来自于配置,用来构造向第三方发起的请求 URL。
3、scopes 来自于配置,是第三方平台给我们授权划定的作用域,可以理解为角色。
4、state 自动生成的,为了防止 csrf 攻击。
5、authorizationRequestUri 向第三方平台发起授权请求的,可以直接通过OAuth2AuthorizationRequest的构建类来设置或者通过上面的authorizationUri等参数来生成。
6、redirectUri 当OAuth2AuthorizationRequest被第三方平台收到后,第三方平台会回调这个 URI 来对授权请求进行响应。

authorizationRequestUri 的构建机制

如果不显式提供authorizationRequestUri就会通过OAuth2AuthorizationRequest中的

①responseType
②clientId
③scopes
④state
⑤redirectUri
⑥additionalParameters

按照下面的规则进行拼接成authorizationUri的参数串,参数串的key和value都要进行 URI 编码。

https://{认证服务器地址}/oauth2/authorize?response_type=code&client_id=test123&state=VWr9pnTYODclkYgmDiCs2w6gE2CVEBH2WZVYCb_4nsc%3D&redirect_uri=http://localhost:8090/projectname/login/oauth2/code/test123

然后OAuth2AuthorizationRequestRedirectFilter负责重定向到authorizationRequestUri向第三方请求授权。

redirectUri

认证服务器收到响应会调用 redirectUri ,回调也是有一定规则的,遵循{baseUrl}/{action}/oauth2/code/{registrationId}的路径参数规则。

1、baseUrl 是从我们/oauth2/authorization请求中提取的基础请求路径。
2、action,有两种默认值login、authorize ,当/oauth2/authorization请求中包含了action参数时会根据action的值进行填充。
3、registrationId 这个就不用多说了。 

3、OAuth2AuthorizationRequestRedirectFilter类

一看到它继承了 OncePerRequestFilter 就知道肯定是他了。甚至它的成员变量包含了用来解析 OAuth2 请求的 OAuth2AuthorizationRequestResolver 。到这里我们的路子就走对了,开始分析这个过滤器,下面是其核心过滤逻辑,这就是我们想要知道的 OAuth2 授权请求是如何被拦截处理的逻辑。

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		try {
			OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request);
			if (authorizationRequest != null) {
				this.sendRedirectForAuthorization(request, response, authorizationRequest);
				return;
			}
		}
		catch (Exception ex) {
			this.unsuccessfulRedirectForAuthorization(request, response, ex);
			return;
		}
		try {
			filterChain.doFilter(request, response);
		}
		catch (IOException ex) {
			throw ex;
		}
		catch (Exception ex) {
			// Check to see if we need to handle ClientAuthorizationRequiredException
			Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
			ClientAuthorizationRequiredException authzEx = (ClientAuthorizationRequiredException) this.throwableAnalyzer
					.getFirstThrowableOfType(ClientAuthorizationRequiredException.class, causeChain);
			if (authzEx != null) {
				try {
					OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request,
							authzEx.getClientRegistrationId());
					if (authorizationRequest == null) {
						throw authzEx;
					}
					this.sendRedirectForAuthorization(request, response, authorizationRequest);
					this.requestCache.saveRequest(request, response);
				}
				catch (Exception failed) {
					this.unsuccessfulRedirectForAuthorization(request, response, failed);
				}
				return;
			}
			if (ex instanceof ServletException) {
				throw (ServletException) ex;
			}
			if (ex instanceof RuntimeException) {
				throw (RuntimeException) ex;
			}
			throw new RuntimeException(ex);
		}
	}

OAuth2AuthorizationRequestRedirectFilter 执行流程:
OAuth2AuthorizationRequestRedirectFilter执行流程
根据这个流程,如果要搞清楚 Spring Security OAuth2 是如何重定向到第三方认证平台的话就要深入研究sendRedirectForAuthorization方法。

二:OAuth2授权请求是如何构建并执行的

1、sendRedirectForAuthorization方法

sendRedirectForAuthorization方法的主要作用就是向授权服务器进行授权重定向访问。它所有的逻辑都和 OAuth2AuthorizationRequest 有关。

往上查找 OAuth2AuthorizationRequest 的详细介绍,然后再往下看。

当授权服务器收到 OAuth2 授权请求后,会将授权的回执通过我方提供的回调请求redirect_uri传递给我们。由于默认情况下回调的路径满足 /login/oauth2/code/* ,所以我们只要找到拦截回调的过滤器就可以知道 Spring Security 是如何处理回调了。通过搜索确认了 OAuth2LoginAuthenticationFilter 就是处理回调的过滤器。

三:OAuth2授权回调的处理机制

1、OAuth2LoginAuthenticationFilter类

第三方认证服务器在调用 redirect_uri 时附加 code 和 state 参数,在被这个Filter拦截后,创建一个待认证凭据 OAuth2LoginAuthenticationToken ,并委托给了AuthenticationManager 进行身份验证。

一旦成功验证,则生成认证凭据 OAuth2AuthenticationToken 和认证客户端对象OAuth2AuthorizedClient。最后, OAuth2AuthenticationToken 返回,并最终存储在SecurityContextRepository 完成认证处理;而 OAuth2AuthorizedClient 被保存到OAuth2AuthorizedClientRepository。流程图如下:

OAuth2LoginAuthenticationFilter执行流程
在这里插入图片描述

2、AuthenticationManager 的认证过程

AuthenticationManager 的实现类 ProviderManager 管理了众多的AuthenticationProvider。每一个 AuthenticationProvider 都只支持特定类型的Authentication,如果不支持将会跳过。另一个作用就是对适配的Authentication进行认证,只要有一个认证成功,那么就认为认证成功,所有的都没有通过才认为是认证失败。认证成功后的Authentication就变成授信凭据,并触发认证成功的事件。认证失败的就抛出异常触发认证失败的事件。

ProviderManager认证Token的流程:
在这里插入图片描述
从这里我们可以看出认证管理器 AuthenticationManager 针对特定的Authentication提供了特定的认证功能,我们可以借此来实现多种认证并存。

3、OAuth2 对应的 AuthenticationProvider

那么 OAuth2 登录有异曲同工之妙,我们需要找到OAuth2LoginAuthenticationToken对应的AuthenticationProvider。这里找到了两个:

1、OAuth2LoginAuthenticationProvider

2、OidcAuthorizationCodeAuthenticationProvider

这两个各自对应的场景是什么呢,OAuth2LoginAuthenticationProvider 中有以下片段:

if (loginAuthenticationToken.getAuthorizationExchange().getAuthorizationRequest().getScopes()
				.contains("openid")) {
	// This is an OpenID Connect Authentication Request so return null
	// and let OidcAuthorizationCodeAuthenticationProvider handle it instead
	return null;
}

意思是说scopes中如果包含了openid就直接返回null,不会被OAuth2LoginAuthenticationProvider处理,而OidcAuthorizationCodeAuthenticationProvider 中正好相反。根据以往文章的脉络OAuth2LoginAuthenticationProvider就是我们需要的。

有兴趣可了解基于OIDC的 OAuth2 认证。

4、OAuth2LoginAuthenticationProvider

OAuth2LoginAuthenticationProvider 实现了授权回调的认证过程:
在这里插入图片描述
从上图中我们可以看出具体认证由OAuth2AuthorizationCodeAuthenticationProvider来负责,认证通过后会去获取用户的信息并封装为 OAuth2User ,最终生成授权成功的 OAuth2LoginAuthenticationToken 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/801101.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IDEA实现SpringBoot项目的自打包自发布自部署

目录 前言 正文 操作背景 自发布 自部署 尾声 🔭 Hi,I’m Pleasure1234🌱 I’m currently learning Vue.js,SpringBoot,Computer Security and so on.👯 I’m studying in University of Nottingham Ningbo China📫 You can reach…

服务器数据恢复—raid5阵列热备盘同步失败导致lun不可用的数据恢复案例

服务器存储数据恢复环境: 华为S5300存储中有一组由16块FC硬盘组建的RAID5磁盘阵列(包含一块热备盘)。 服务器存储故障: 该存储中的RAID5阵列1块硬盘由于未知原因离线,热备盘上线并开始同步数据,数据同步到…

[iOS]内存分区

[iOS]内存分区 文章目录 [iOS]内存分区五大分区栈区堆区全局区常量区代码区验证内存使用注意事项总结 函数栈堆栈溢出栈的作用 参考博客 在iOS中,内存主要分为栈区、堆区、全局区、常量区、代码区五大区域 还记得OC是C的超类 所以C的内存分区也是一样的 iOS系统中&a…

sping总览

一、spring体系 1. spring是什么? 轻量级的开源的J2EE框架。它是一个容器框架,主要实现了ioc,同时又通过aop实现了面向切面编程,它又是一个中间层框架(万能胶)可以起一个连接作用,比如说把myba…

Django任务管理:项目定时执行及简单管理界面

1、用django-admin命令创建一个Django项目 django-admin startproject task_manager 2、进入到项目下用命令创建一个应用 cd task_manager python manage.py startapp tasks 3、进入models.py定义数学模型 第2步得到的只是应用的必要空文件,要开始增加各文件实际…

【java计算机毕设】图书馆书库管理系统java MySQL ssm vue maven项目设计源代码+文档PPT 小组作业

目录 1项目功能 2项目介绍 3项目地址 1项目功能 【java计算机毕设】图书馆书库管理系统java MySQL ssm vue maven项目设计源代码文档PPT 小组作业 2项目介绍 系统功能: 图书馆书库管理系统包括管理员、用户俩种角色。 管理员功能包括个人中心模块用于修改个人信…

y7000p 2020h风扇狂转解决办法

方法1(不推荐) 电源选项限制99% 这两个100%都修改为99%,这样可以限制睿频,但是不推荐,因为会显得有些卡顿注意方法2只在win10下尝试过,win11需要你们自测 方法2(强烈推荐!&…

2.3-基于RNN的语言模型的学习与评价

文章目录 1引言2 RNNLM的代码实现2.1初始化2.2前向和反向传播 3语言模型的一个评价指标-困惑度3.1数据量为一时困惑度的计算3.2数据量为多个的时候困惑度的计算 4 基于PTB数据集的RNNLM的学习的实现 1引言 所有代码位于:https://1drv.ms/f/s!AvF6gzVaw0cNjpx9BAtQYG…

docker-compose部署redis-exporter

一、安装prometheus 1、安装 version: 3.1services:redis-exporter:image: bitnami/redis-exporter:latestcontainer_name: redis-exporterports:- 9121:9121environment:TZ: Asia/Shanghaicommand:- --redis.addrredis://127.0.0.1:6379# - --redis.passwordlabels:org.labe…

如何设计统计量及相关假设检验

一、如何设置H0和H1假设 谁做H0,谁做H1,在统计学的假设检验里是有约定俗成的规定的。即:status quo(默认/现状)是H0,而新观点或试图challenge现状的是H1。H1也叫research hypothesis,所以我们做…

Postman、Apifox、Apipost用哪个?

Postman、Apifox、Apipost都是流行的API接口管理工具,它们各自具有不同的特点和优势,因此哪个更好用取决于具体的使用场景和需求。以下是对这三个工具的比较分析: 一、Postman 特点与优势: 支持多种请求方式:包括GE…

Gartner发布终端安全运营指南:有效终端安全运营的三大关键

孤立的终端管理团队使用专门的工具和策略,这会产生不必要的开支、降低容量、增加风险并降低员工体验。I&O 领导者必须立即采取行动,团结终端管理团队、工具和策略,以取得成功。 主要发现 Gartner 客户互动和最近的一项调查表明&#xff0…

CORDIC Translate

随便记录一下下: Cordic IP核使用说明以及避坑记录-CSDN博客 本次只用到了Translate,记录一下自己遇到的坑坑 实际配置: timescale 1ns / 1nsmodule cordic_tb();reg clk;wire m_axis_dout_tvalid;reg s_axis_cartesian_tvalid 0;wire [31…

Apollo docker-compose

来源 https://www.apolloconfig.com/#/zh/deployment/quick-start-docker 路径 /usr/apollo Sql 自己复制 Vim docker-compose.yml #如果安装过了 记得删除mysql 历史文件 rm -r /var/lib/mysql version: 2.1services:apollo-quick-start:image: nobodyiam/apollo-quick…

《javeEE篇》--多线程(1)

进程 在讲线程之前我们先来简单了解一下进程 什么是进程 进程是操作系统对一个正在运行的程序的一种抽象,又或者说,可以把进程看作程序的一次运行过程(通俗的讲就是跑起来的程序)。 而且在操作系统内部,进程是资源分配的基本单位 PBC P…

食品企业销售管理体系升级的关键

盖世食品(股票代码:836826)是国家级农业产业化重点龙头企业,国家高新技术企业,拥有300种产品,1000SKU。从海洋蔬菜、营养菌菇、健康素菜到海珍味系列和鱼子系列,消费者经常可以从各大餐饮连锁店里吃到这家公司制作的凉…

前端开发(基础)

目录 一、Web前端项目初始化 环境准备 创建项目 前端工程化配置 引入组件库 开发规范 全局通用布局 基础布局结构 全局底部栏 动态替换内容 全局顶部栏 通用路由菜单 支持多套布局 请求 请求工具库 全局自定义请求 自动生成请求代码 全局状态管理 全局权限管…

跟着操作,解决iPhone怎么清理内存难题

在如今智能手机功能日益强大的时代,我们使用手机拍照、录制视频、下载应用、存储文件等操作都会占用手机内存。当内存空间不足时,手机运行会变得缓慢,甚至出现卡顿、闪退等现象。因此,定期清理iPhone内存是非常必要的。那么&#…

最新 taro v3 运行,报错 Error: [object Object] is not a PostCSS plugin 解决办法

报错如下: Error: [object Object] is not a PostCSS plugin 解决办法:pnpm install postcss -D 重新安装 postcss 依赖,重新运行即可。 结果:顺利运行

2000-2023年上市公司融资约束WW指数(含原始数据+计算结果)

2000-2023年上市公司融资约束WW指数(含原始数据计算结果) 1、时间:2000-2023年 2、来源:上市公司年报 3、指标:证券代码、证券简称、统计截止日期、是否发生ST或*ST或PT、是否发生暂停上市、行业代码、行业名称、上…