Spring Security Oauth2源码分析

news2024/11/23 13:17:13

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.coloradmin.cn/o/1931054.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

hiphop音乐风格分类有几种 怎么使用FL Studio制作Hip Hop音乐 hiphop音乐制作教程

Hip Hop音乐是一类新潮的音乐风格,融合了许多不同的文化元素和音乐表达方式。嘻哈(hip hop)诞生于美国贫民区街头的一种文化形式,一般的说法认为它诞生于美国纽约布朗克斯。嘻哈首先在纽约市北部布朗克斯市区的非裔及拉丁裔青年之…

kali进行host碰撞实验

目录 在kali上安装docker,完成环境搭建,进行host碰撞实验 更新软件包列表 下载阿里云的镜像源 添加 GPG 密钥并添加更新源 安装 Docker 安装成功检验 开启docker 检查docker状态 拉取镜像 在kali访问ip(加端口) 下载压缩…

京东超级18活动入口!京东超级18活动怎么玩?

京东推出新活动,低价的持续性项目“京东超级18”。 活动入口: 京东超级18,领60元大额补贴! 口令直达:14:/京东超级18¥NF5fuBPWIIaHRiMr¥,↗鯨○Dσσδng。 手机京东搜索&#xf…

Claude 3.5 Sonnet模型发布,对比ChatGPT4o孰强孰弱

Anthropic 这家生而为打击 OpenAI 安全问题的公司,正式发布了Claude 3.5 Sonnet模型! 用官网的话就是: 今天,我们推出了 Claude 3.5 Sonnet,这是我们即将推出的 Claude 3.5 型号系列中的第一个版本。Claude 3.5 Sonne…

vscode 打开远程bug vscode Failed to parse remote port from server output

vscode 打开远程bug vscode Failed to parse remote port from server output 原因如图: 解决:

02 Git环境搭建

第2章:Git环境搭建 一、Git下载和安装 ​ 官网:Git (git-scm.com) 一)安装主程序 ​ 准备安装包,双击安装 ​ 开始安装 ​ 选择安装位置 ​ 选择需要安装的组件(默认) ​ 选择文件夹菜单 ​ 选择编辑器&…

Word参考文献交叉引用

前言 Word自带交叉引用功能,可在正文位置引用文档内自动编号的段落,同时创建超链接,适用于参考文献的引用。使用此方法对参考文献进行引用后,当参考文献的编号发生变化时,只需要更新域即可与正文中的引用相对应。下文…

sysbench测试CPU

版本:sysbench1.1 简介 sysbench 对 CPU 测试的原理是通过创建多个线程,并让它们执行计算密集型任务来测试 CPU 性能。它会记录每个线程执行任务所需的时间,并计算出整个测试过程的平均值、最小值和最大值,甚至输出直方图展示延迟…

SpringBoot下的定时魔法:揭秘@Scheduled注解的无限可能

在这个快节奏的时代,自动化与定时任务成为了提升效率的不二法门。而在Java的Spring Boot框架中,Scheduled注解就像是一位精通时间魔法的巫师,悄无声息地让你的应用按部就班地执行着各种定时任务。今天,就让我们一起揭开它的神秘面…

【Qt 常用控件】带你进一步了解常用控件

文章目录 1. Push Button2. Radio Button3. Check Box4. 显示类控件5. 输入类控件 QLineEdit5.1 正则表达式5.2 验证器 - 验证手机号5.3 验证两次密码是否一致5.3 明文的显示密码 1. Push Button 🐧给按钮设置图标 🐧 注意:设置快捷键需要在…

【运维资料大全】运维全套资料整理(word原件完整版)

信息安全类、运维类资料整合: 1. 等保测评模板 2. 安全检查表(IDS,Linux,数据库,Tomcat,防火墙等) 3. 服务器安全巡检表 4. 网络定向攻击风险分析表 5. 应用系统常规检测表 6. 企业漏洞管理50个…

Web开发:卡片翻转效果(HTML、CSS)

目录 一、实现效果 二、完整代码 三、实现过程 1、页面结构 2、初始样式 3、翻转效果 4、图片大小问题 一、实现效果 如下图所示,当鼠标移入某个盒子,就反转这个盒子,并显示其背面的内容——卡片翻转效果; 卡片翻转效果 二…

【HarmonyOS】HarmonyOS NEXT学习日记:二、ArkTs语法

【HarmonyOS】HarmonyOS NEXT学习日记:二、ArkTs语法 众所周知TS是JS的超集,而ArkTs则可以理解为是Ts的超集。他们的基础都基于JS,所以学习之前最好就JS基础。我的学习重点也是放在ArkTs和JS的不同点上。 文章主要跟着官方文档学习,跳过了一…

Golang | Leetcode Golang题解之第234题回文链表

题目: 题解: func reverseList(head *ListNode) *ListNode {var prev, cur *ListNode nil, headfor cur ! nil {nextTmp : cur.Nextcur.Next prevprev curcur nextTmp}return prev }func endOfFirstHalf(head *ListNode) *ListNode {fast : headslo…

多媒体软件开发选择Animate软件还是Unity3D软件?

以下内容可能有一些片面,因为多媒体软件开发平台有很多,因为接触Animate和Unity3D比较多,所以这里仅对这两款进行分析! Animate软件与Unity3D软件都是经常在多媒体展馆中用来制作互动展示内容的,对于这两种开发平台&a…

用AI生成Springboot单元测试代码太香了

你好,我是柳岸花开。 在当今软件开发过程中,单元测试已经成为保证代码质量的重要环节。然而,编写单元测试代码却常常让开发者头疼。幸运的是,随着AI技术的发展,我们可以利用AI工具来自动生成单元测试代码,极…

【系统架构设计师】十一、系统架构设计(中间件|典型应用架构)

目录 九、中间件 9.1 基础概念 9.2 中间件分类 十、典型应用架构 10.1 J2EE和四层结构 10.2 JSPServletJavaBeanDAO 10.3 .NET和J2EE之争 往期推荐 历年真题练习 九、中间件 之前总提到中间件,那么中间件到底是什么?在系统架构中又扮演者什么角…

摸鱼大数据——Kafka——kafka tools工具使用

可以在可视化的工具通过点击来操作kafka完成主题的创建,分区等操作 注意: 安装完后桌面不会有快捷方式,需要去电脑上搜索,或者去自己选的安装位置找到发送快捷方式到桌面! 连接配置 创建主题 删除主题 主题下的数据查看 数据显示问题说明 修改工具的数据显示类型 发…

【C++】vector的认识与使用

vector的认识与使用 认识vectorvector的使用Member functions(成员函数)构造函数(constructor)析构函数(destructor)赋值构造函数(operator) Iterators(迭代器)beginendrbeginrend Capacity(容量)sizemax_s…

zephyr设置BLE广播数据实例

目录 实例1:静态开启广播数据实例2:动态更改广播数据实例3:创建可连接的广播 实例1:静态开启广播数据 新建一个hello world的工程模板。 在prj.conf中开启蓝牙 CONFIG_BTy这个宏,默认会开启广播支持 ( BT_BROADCAS…