spring Security源码讲解-WebSecurityConfigurerAdapter

news2024/9/22 17:30:11

使用security我们最常见的代码:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .permitAll();

        http.authorizeRequests()
                .antMatchers("/oauth/**", "/login/**","/logout/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf()
                .disable();

    }

    @Bean
    public PasswordEncoder getPasswordWncoder(){
        return new BCryptPasswordEncoder();
    }
}

这段代码源头是WebSecurityConfiguration这个类下的

	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		return webSecurity.build();
	}

首先解释一下这部分代码的作用是返回内置过滤器和用户自己定义的过滤器集合,当然下一节讲解security是怎么使用拿到的过滤器。上述代码有一个关键的对象webSecurity,这里先不说他的作用,webSecurity是通过setFilterChainProxySecurityConfigurer方法创建的实例对象。

setFilterChainProxySecurityConfigurer

webSecurity来自WebSecurityConfiguration中的

	@Autowired(required = false)
	public void setFilterChainProxySecurityConfigurer(
			ObjectPostProcessor<Object> objectPostProcessor,
			@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
			throws Exception {
		webSecurity = objectPostProcessor
				.postProcess(new WebSecurity(objectPostProcessor));
		if (debugEnabled != null) {
			webSecurity.debug(debugEnabled);
		}

		Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);

		Integer previousOrder = null;
		Object previousConfig = null;
		for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
			Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
			if (previousOrder != null && previousOrder.equals(order)) {
				throw new IllegalStateException(
						"@Order on WebSecurityConfigurers must be unique. Order of "
								+ order + " was already used on " + previousConfig + ", so it cannot be used on "
								+ config + " too.");
			}
			previousOrder = order;
			previousConfig = config;
		}
		for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
			webSecurity.apply(webSecurityConfigurer);
		}
		this.webSecurityConfigurers = webSecurityConfigurers;
	}

setFilterChainProxySecurityConfigurer方法使用的是参数注入的方式,List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)参数会依赖查询SecurityConfigurer实例。
好的我们现在再回头看我们自己定义的SecurityConfig类是继承于WebSecurityConfigurerAdapter ,
在这里插入图片描述
可以看到WebSecurityConfigurerAdapter 实现WebSecurityConfigurer接口

在这里插入图片描述
WebSecurityConfigurer又继承SecurityConfigurer,T此时是WebSecurityConfigurer传递过来的WebSecurity,因此满足SecurityConfigurer<Filter, WebSecurity>,所以也会被setFilterChainProxySecurityConfigurer方法依赖查询到,下面我们debug一下
在这里插入图片描述
有人说这个不能保证就是我们定义类,那好我们给我们自己的类加个标识属性 securityName=“securityName”

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private String securityName="securityName";
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .permitAll();

        http.authorizeRequests()
                .antMatchers("/oauth/**", "/login/**","/logout/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf()
                .disable();

    }
}

在这里插入图片描述
因此就是我们自己定义的类实例被成功依赖查询到作为参数。
好的我们现在接着回到WebSecurityConfiguration类的setFilterChainProxySecurityConfigurer方法

在这里插入图片描述

将所有SecurityConfigurer实例作为参数调用webSecurity的apply属性

在这里插入图片描述

继续看add方法
在这里插入图片描述

会将SecurityConfigurer添加到webSecurity的configurers属性中。以上过程都是在讲收集SecurityConfigurer并装配到webSecurity的configurers属性。下面继续将我们的源头函数springSecurityFilterChain是怎么使用webSecurity的

springSecurityFilterChain

	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		return webSecurity.build();
	}

主要关心这行代码webSecurity.build()

	public final O build() throws Exception {
		if (this.building.compareAndSet(false, true)) {
			this.object = doBuild();
			return this.object;
		}
		throw new AlreadyBuiltException("This object has already been built");
	}

继续看doBuild()

	@Override
	protected final O doBuild() throws Exception {
		synchronized (configurers) {
			buildState = BuildState.INITIALIZING;

			beforeInit();
			init();

			buildState = BuildState.CONFIGURING;

			beforeConfigure();
			configure();

			buildState = BuildState.BUILDING;

			O result = performBuild();

			buildState = BuildState.BUILT;

			return result;
		}
	}

doBuild分别调用了init(),configure(),performBuild()

init()

在这里插入图片描述

这里调用了getConfigurers()方法

在这里插入图片描述

this.configurers就是在setFilterChainProxySecurityConfigurer方法中调用webSecurity.apply(webSecurityConfigurer)将SecurityConfigurer存放的位置,这里做的就是将我们的SecurityConfigurer实例存入一个数组中。好到回到我们刚才的位置init()方法

private void init() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.init((B) this);
		}

		for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
			configurer.init((B) this);
		}
	}

可以清除的看到遍历所有的SecurityConfigurer实例并调用其init(WebSecurity webSecurity)方法。那我们现在看看,我们自己定义的SecurityConfigurer实例init(WebSecurity webSecurity)方法具体的是怎样执行的。
首先找到

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private String securityName="securityName";
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .permitAll();

        http.authorizeRequests()
                .antMatchers("/oauth/**", "/login/**","/logout/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf()
                .disable();

    }
}

alt+鼠标左键点击configure(HttpSecurity http)快速定位
在这里插入图片描述
在getHttp()方法内,接着网上找alt+鼠标左键点击getHttp,
在这里插入图片描述

final HttpSecurity http = getHttp();
		web.addSecurityFilterChainBuilder(http)

在这里插入图片描述

原来是创建HttpSecurity实例并将对应的HttpSecurity 放入到webSecurity中,因此我们这里可以得出通过调用webSecurity的init()方法,将所有依赖查询到SecurityConfigurer实例的httpSecurity实例存放到webSecurity的securityFilterChainBuilders集合属性中。原来我们定义的

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .permitAll();

        http.authorizeRequests()
                .antMatchers("/oauth/**", "/login/**","/logout/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf()
                .disable();

    }

    @Bean
    public PasswordEncoder getPasswordWncoder(){
        return new BCryptPasswordEncoder();
    }
}

这段代码就是这样被用起来的,但是还没结束。继续看webSecurity的configure方法。

configure

在这里插入图片描述
调用的都是SecurityConfigurer实例的configure(webSecurity)方法,这个方法具体做了什么呢?

我们只需要关心SecurityConfigurer的configure方法在这里可以拿到webSecurity。继续看performBuild()

performBuild

注意我们将的init(),configu(),performBuild都是webSecurity的成员方法,因此
在这里插入图片描述
先看一个简单的功能
在这里插入图片描述
debugEnabled控制了日志打印,debugEnabled又是webSecurity属性,我们又能在SecurityConfigurer的configure(webSecurity)方法拿到webSecurity,是不是我们就可以在configure(webSecurity)设置debugEnabled,好的我们尝试一下。

没有设置的效果
在这里插入图片描述
添加修改代码

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
        web.debug(true);
    }

在这里插入图片描述
在这里插入图片描述
这个属性不止这么简单,他也是一个条件属性,控制DebugFilterDebugFilter是否执行,如果为true就行执行,否则就不执行。,但是这个功能不是我们主要讲的。回到performBuild()方法,我们的重点代码部分
在这里插入图片描述
securityFilterChainBuilders不就是存所有SecurityConfigurer实例的htttpSecurity集合属性。
这里就是遍历所有的httpSecurity属性,调用其的build方法。此时调用的httpSecurity的build,因此我们找到httpSecurity的performBuild()
在这里插入图片描述
requestMatcher和filters是什么?记住这两个属性并带着疑问回到我们常见的代码

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private String securityName="securityName";
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .permitAll();

        http.authorizeRequests()
                .antMatchers("/oauth/**", "/login/**","/logout/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf()
                .disable();

    }
}

在这里插入图片描述
在这里插入图片描述
原来filters就是我们定义的过滤器。接着探索requestMatcher是什么
在这里插入图片描述
我们先看 http.authorizeRequests()方法

在这里插入图片描述
接着看getOrApply()方法

在这里插入图片描述
继续看apply()
在这里插入图片描述
接着看add()
在这里插入图片描述

原来configurer是放入到了HttpSecurity的configurers中,是不是此时有点明白但又有点不明白,总结:我们自己定义的WebSecurityConfigurerAdapter放入到了webSecurity的configurers属性中,而在WebSecurityConfigurerAdapter调用http.formLogin(),http.authorizeRequests()创建的configurer放到各自WebSecurityConfigurerAdapter实例的httpSecurity实例的configurers中。

其实我们查看类也能看到WebSecurityConfigurerAdapter和http.formLogin(),http.authorizeRequests()产生的configurer都继承于SecurityConfigurer,而webSecurity和httpSecurity都继承于和实现相同的类和接口。

所以我们也可以得出httpSecurity调用configure方法其实是调用的http.formLogin(),http.authorizeRequests()产生的configurer实例的configurer(HttpSecurity)方法。

requestMatcher是什么呢
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
回到

在这里插入图片描述查看antMatchers方法
在这里插入图片描述

查看RequestMatchers.antMatchers(antPatterns)的antMatchers方法
在这里插入图片描述
在这里插入图片描述

原来RequestMatchers.antMatchers(antPatterns)方法将我们的路径封装成了RequestMatcher集合,回到
在这里插入图片描述
查看chainRequestMatchers方法
这里我们要回到http.authorizeRequests()
在这里插入图片描述
查看getRegistry方法返回的是ExpressionInterceptUrlRegistry类型
在这里插入图片描述ExpressionInterceptUrlRegistry该类继承于ExpressionUrlAuthorizationConfigurer.AbstractInterceptUrlRegistry
在这里插入图片描述
AbstractInterceptUrlRegistry继承AbstractConfigAttributeRequestMatcherRegistry
在这里插入图片描述
所以这里chainRequestMatchers是AbstractConfigAttributeRequestMatcherRegistry的方法在这里插入图片描述在这里插入图片描述
查看chainRequestMatchersInternal方法,chainRequestMatchersInternal此时是ExpressionUrlAuthorizationConfigurer的成员方法

在这里插入图片描述
因此返回的是 new AuthorizedUrl(requestMatchers);
回到
在这里插入图片描述

继续看AuthorizedUrl的permitAll()方法
在这里插入图片描述
查看access方法
在这里插入图片描述
查看interceptUrl方法
在这里插入图片描述

整个流程
1.创建WebSecurity
2.调用WebSecurity的configure方法,创建HttpSecurity
3.调用WebSecurity的performBuild方法
4.调用HttpSecurity的configure方法,回调consfigure的consfigure(HttpSecurity)方法添加各自的过滤器到HttpSecurity的filters
5.打包HttpSecurity过滤器返回并添加到WebSecurity的securityFilterChains属性
6.封装WebSecurity的securityFilterChains属性为FilterChainProxy
在这里插入图片描述
最终返回包含所有过滤器的 FilterChainProxy实例
在这里插入图片描述

FilterChainProxy使用过程

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

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

相关文章

【Python】DataFrame 使用 concat 横向拼接出现两行问题

问题 在使用 DataFrame 中 concat 横向拼接两个只有一行的 DataFrame 时&#xff0c;最终的结果有两行。 如下图&#xff1a; 原始的 df 分别为&#xff1a; 指定横向合并后是&#xff1a; 这里可以看到是横向拼接了&#xff0c;但是并没有真正意义的横向拼接&#xff0c;而…

AI数字人虚拟现实产业的发展现状与展望

AI数字人虚拟现实产业是当今科技领域备受瞩目的发展方向之一。随着人工智能和虚拟现实技术的迅猛发展&#xff0c;人们对于数字形象的需求不断增加&#xff0c;AI数字人虚拟现实产业正应运而生。本文将从产业现状和未来展望两个方面来描绘AI数字人虚拟现实产业的发展。 首先&a…

编程学习课前准备

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 浏览器和文本编辑器安装 数据分析三大软件安装 操作系统要求 查看Windows系统版本和位数 查看操作系统账户信息 Windows目录显式设置 命令行界面使用 打开命令行 方法一&#xff1a; 方法二&#xff1a; 方法…

MT6762芯片性能参数介绍_MTK联发科处理器

MT6762采用台积电 12 nm FinFET 制程工艺&#xff0c;8* Cortex-A53架构&#xff0c;搭载Android9.0/11.0/12.0操作系统&#xff0c;主频最高达2.0GHz&#xff0c;提供更高阶的功能和出色的体验。 搭载PowerVR GE8329 GPU&#xff0c;运行频率高达 650MHz&#xff0c;实现 20&a…

小游戏选型(一):游戏化设计助力直播间互动和营收

一、社交直播间小游戏火爆 大家好&#xff0c;作为一个技术宅和游戏迷&#xff0c;今天来聊聊近期爆火的社交直播间小游戏的潮流。喜欢冲浪玩社交产品的小伙伴会发现&#xff0c;近期各大平台都推出了直播间社交小游戏&#xff0c;直播间氛围火爆&#xff0c;小游戏玩法简单&a…

Java程序员面试-场景篇

前言 裁员增效潮滚滚而来&#xff0c;特总结一些实际场景方案的面试题&#xff0c;希望对大家找工作有一些帮助。 注册中心 题目&#xff1a; 有三台机器&#xff0c;分别部署了微服务A、微服务B、注册中心&#xff0c;其中A和B都有服务接口提供并正常注册到了注册中心&…

Halcon 模板匹配基于轮廓(形状)

文章目录 halcon 案例 基于缩放比halcon 案例 测单个剃须刀片Halcon 案例创建匹配模板Halcon 通过图像处理创建模型 ROI模型Halcon 亚像素识别Halcon 识别不等比例的图像Halcon 匹配包装袋案例Halcon 创建模板进行匹配Halcon 案例模板匹配与测量Halcon 多模板与多图像的匹配 ha…

CMake入门教程【核心篇】导入外部库Opencv

😈「CSDN主页」:传送门 😈「Bilibil首页」:传送门 😈「动动你的小手」:点赞👍收藏⭐️评论📝 文章目录 环境准备示例:在Windows上配置OpenCV路径示例:在Linux上配置OpenCV路径环境准备 首先确保你的系统中安装了CMake。可以通过以下命令安装: Windows: 下载并…

猴子选大王

思路&#xff1a;首先举个例子&#xff1a;当N 5 时 1 2 3 4 5 3 3 3 3 输出4 请观看代码 …

光纤知识总结

1光纤概念&#xff1a; 光导纤维&#xff08;英语&#xff1a;Optical fiber&#xff09;&#xff0c;简称光纤&#xff0c;是一种由玻璃或塑料制成的纤维&#xff0c;利用光在这些纤维中以全内反射原理传输的光传导工具。 微细的光纤封装在塑料护套中&#xff0c;使得它能够…

定时器中断控制的独立式键盘扫描实验

#include<reg51.h> //包含51单片机寄存器定义的头文件 sbit S1P1^4; //将S1位定义为P1.4引脚 sbit S2P1^5; //将S2位定义为P1.5引脚 sbit S3P1^6; //将S3位定义为P1.6引脚 sbit S4P1^7; //将S4位定义为P1.7引脚 unsigned char keyval; /…

在黑马程序员大学的2023年终总结

起笔 时间真快&#xff0c;转眼又是年末。是时候给2023做个年终总结了&#xff0c;为这一年的学习、生活以及成长画上一个圆满的句号。 这一年相比去年经历了很多事情&#xff0c;接下来我会一一说起 全文大概4000字&#xff0c;可能会占用你15分钟左右的时间 经历 先来给大…

Python学习之路-Hello Python

Python学习之路-Hello Python Python解释器 简介 前面说到Python是解释型语言&#xff0c;Python解释器的作用就是用于"翻译"Python程序。Python规定了一个Python语法规则&#xff0c;根据该规则可编写Python解释器。 常见的Python解释器 CPython&#xff1a;官方…

哈希-力扣01两数之和

题目 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺…

云仓酒庄的品牌雷盛红酒LEESON分享什么是“小农香槟”?

云仓酒庄的品牌雷盛红酒LEESON分享说起香槟&#xff0c;第一时间会想到法国&#xff0c;因为只有法国的起泡酒才能叫“香槟”。那么&#xff0c;什么又是“小农香槟”呢&#xff1f; 小农香槟是相对大厂香槟而命名的&#xff0c;是指葡萄果农自产、自酿、自销的香槟&#xff0…

TS中的类

目录 ES6的类 类的概念 类的构成 类的创建 声明 构造函数 定义内容 创建实例 TS中的类 类声明 构造函数 属性和方法 实例化类 继承 访问修饰符 public private protected 成员访问修饰符的使用原则 访问器 只读成员与静态成员 readonly static 修饰符总…

MySQL之导入导出远程备份

目录 一. navicat导入导出 二. mysqldump命令导入导出 导入 导出 三. load data infile命令导入导出 导入 导出 四. 远程备份 导入 导出 思维导图 一. navicat导入导出 导入&#xff1a;右键➡运行SQL文件 导出&#xff1a;选中要导出的表➡右键➡转储SQL文件➡数据和结…

【PB续命07】JDBC连接达梦数据库

JDBC(Java DataBase Connectivity) 称为Java数据库连接&#xff0c;它是一种用于数据库访问的应用程序API&#xff0c;由一组用Java语言编写的类和接口组成&#xff0c;有了JDBC就可以用同一的语法对多种关系数据库进行访问&#xff0c;而不用担心其数据库操作语言的差异。 有了…

基于Python的货币识别技术实现

目录 介绍本文的目的和意义货币识别技术的应用场景 货币识别的基本原理图像处理技术在货币识别中的应用特征提取方法&#xff1a;SIFT、HOG等支持向量机&#xff08;SVM&#xff09;分类器的使用 实现过程数据集的收集和预处理特征提取和训练分类器 参考文献 介绍 本文的目的和…

Spring事务控制见解6

7.Spring事务控制 7.1.事务介绍 7.1.1.什么是事务&#xff1f; 当你需要一次执行多条SQL语句时&#xff0c;可以使用事务。通俗一点说&#xff0c;如果这几条SQL语句全部执行成功&#xff0c;则才对数据库进行一次更新&#xff0c;如果有一条SQL语句执行失败&#xff0c;则这…