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的参数,默认值是loginString 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 ClientAuthorizationRequiredExceptionThrowable[] 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、OAuth2LoginAuthenticationProvider2、OidcAuthorizationCodeAuthenticationProvider

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

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

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

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

4、OAuth2LoginAuthenticationProvider

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

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

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

相关文章

元宇宙深入解析

元宇宙(Metaverse)是一个新兴的概念,它激发了技术专家、艺术家和商业领袖的无限想象。它代表着数字互动的新前沿,提供了一个平行的数字宇宙,用户可以在其中实时互动,超越物理世界的限制。 元宇宙是什么&am…

前端开发体系+html文件详解

目录 html骨架 body主体内基本元素 基本元素 超文本(超链接跳转) 锚点 图片标签 列表标签 表格标签 框架标签(窗口标签) 音频标签 视频标签 VScode编译器 输入框 字体样式 实例展示: 首先简要介绍前端的整…

Windows与Linux双机热备软件推荐

网络数据安全在如今信息化的时代越来越变得举足轻重,因此服务器维护和管理也成为企业健康稳定运营的一项重要工作。但实际情况是很多公司并没有配备专业的运维人员,一般都会通过一些管理软件维护或者主机托管给服务商。整理6款服务器的Windows与Linux双机…

【Vue】Vue3 安装 Tailwind CSS 入门

初始化 Vue 3 项目 npm install -g vue/cli vue create my-project安装 Tailwind CSS 进入你的项目目录,然后安装 Tailwind CSS 和其依赖项: npm install -D tailwindcss postcss autoprefixer配置 PostCSS Tailwind CSS 需要通过 PostCSS 进行处理。…

类和对象的简述(c++篇)

开局之前,先来个小插曲,放松一下: 让我们的熊二来消灭所有bug 各位,在这祝我们: 放松过后,开始步入正轨吧。爱学习的铁子们: 目录: 一类的定义: 1.简述: 2…

飞睿智能UWB Tag蓝牙防丢器标签,宠物安全新升级,5cm精准定位测距不迷路

宠物早已成为许多家庭不可或缺的一员,它们用无条件的爱温暖着我们的心房,陪伴我们度过每一个平凡而温馨的日子。然而,随着宠物活动范围的扩大和外界环境的复杂多变,宠物走失的风险也随之增加。每一次出门遛弯,都像是心…

补充.IDEA的使用

首先我们要了解在idea中Java工程由项目(project)、模块(module)包(package)、类(class)组成。 他们之间的关系是project包含module包含package包含class。 所以我们要按照先建一个pr…

心理健康服务小程序的设计

管理员账户功能包括:系统首页,个人中心,学生管理,最新资讯管理,心理产品管理,产品分类管理,音乐理疗管理,试题管理 微信端账号功能包括:系统首页,心理产品音…

YOLOV5的输出[1,25200,85]如何理解和解析

1、25200代表着检测框的数量,比如我们取出第一个检测框a,也就是[1,1,85],取出来之后我们解析85,前五个为box的中点坐标、长宽值以及置信,后面80我们取Max(80个类别)中最大值&#xf…

Facebook:数字时代的社交瑰宝

在当今数字化飞速发展的时代,社交媒体已经成为人们日常生活中不可或缺的一部分,而Facebook作为其中的领军者,不仅连接了全球数十亿的用户,更深刻地改变了人们的社交方式和生活方式。本文将探讨Facebook如何成为数字时代的社交瑰宝…

[Cesium for Supermap] 加载3dTiles,点击获取属性

代码: // 设为椭球var obj [6378137.0, 6378137.0, 6356752.3142451793];Cesium.Ellipsoid.WGS84 Object.freeze(new Cesium.Ellipsoid(obj[0], obj[1], obj[2]));var viewer new Cesium.Viewer(cesiumContainer);var scene viewer.scenescene.lightSource.ambi…

数码科技有限公司企业网站源码系统 前后端分离 带完整的安装代码包以及搭建部署教程

系统概述 数码科技有限公司企业网站源码系统是一套基于最新技术开发的网站解决方案,专注于为企业提供稳定、可扩展且易于维护的网站平台。该系统结合了现代网站设计的最佳实践和用户体验原则,确保网站能够在各种设备和浏览器上流畅运行,同时…

Data类中的常用方法

Calender类 java.util.Calendar是一个抽象的基类,创建对象需要使用静态方法Calendar.getInstance()完成。通过Calendar对象可以获得详细的日历信息,例如年、月、日、小时、分和秒,Calendar的子类可以实现特定的日历系统。 当前时间 Calenda…

前端开发日记——在MacBook上配置Vue环境

前言 大家好,我是来自CSDN的寄术区博主PleaSure乐事。今天是开始学习vue的第一天,我使用的编译器是vscode,浏览器使用的是谷歌浏览器,后续会下载webstorm进行使用,当前学习阶段使用vscode也是可以的,不用担…

LeetCode 4, 92, 155

目录 4. 寻找两个正序数组的中位数题目链接标签思路代码 92. 反转链表 II题目链接标签思路反转部分链表寻找 prev为什么使用 sentinel 代码 155. 最小栈题目链接标签思路栈的实现最小值的实现 代码 4. 寻找两个正序数组的中位数 题目链接 4. 寻找两个正序数组的中位数 标签 …

Qt类 | QPushButton类详解

文章目录 一、QPushButton介绍二、Properties(属性)三、Public Functions(公共函数)1.构造函数--构造按钮对象2.autoDefault与setAutoDefault函数--获取/设置按钮的自动默认状态3.isDefault与setDefault函数-- 获取/设置按钮的默认…

服务器的80和443端口关闭也能申请SSL证书

一、简介 在服务器的80和443端口关闭的情况下,确实可以申请SSL证书,但申请过程和方法会根据证书类型和验证方式的不同而有所差异。 通常如果是网站域名申请SSL证书,哪怕服务器的80、443端口都打不开,也可以通过DNS解析的方式来验…

【bypy】服务器代码定期同步到百度网盘

☆ 服务器代码定期同步到百度网盘 - 问题描述 代码的备份是一个重要的事情,可能经常会换服务器,也可能服务器会崩溃。这里教如何将代码同步到百度网盘。当然,智能同步到百度网盘指定的apps目录下 ★ 解决方案 step1. 安装bypy库 首先要确保…

Qt Style Sheets-设计器集成

设计器集成 Qt Designer(Qt Designer)是一个出色的工具,用于预览样式表。您可以在 Designer 中右键单击任何小部件,并选择“更改样式表...”来设置样式表。 在 Qt 4.2 及更高版本中,Qt Designer 还包括一个样式表语法…

layui 让table里的下拉框不被遮挡

记录:layui 让table里的下拉框不被遮挡 /* 这个是让table里的下拉框不被遮挡 */ .goods_table .layui-select-title,.goods_table .layui-select-title input{line-height: 28px;height: 28px; }.goods_table .layui-table-cell {overflow: visible !important; }.…