Spring Security 6.1.x 系列(4)—— 基于过滤器链的源码分析

一、自动配置

在 Spring Security 6.1.x 系列(1)—— 初识Spring Security 中我们只引入spring-boot-starter-security 依赖,就可以实现登录认证,这些都得益于Spring Boot 的自动配置。

spring-boot-autoconfigure模块中集成了对Spring Security的自动配置:

在这里插入图片描述

默认的配置是由 SecurityAutoConfigurationUserDetailsServiceAutoConfigurationSecurityFilterAutoConfiguration这三个自动配置类实现的。

二、SecurityAutoConfiguration

SecurityAutoConfiguration 主要是导入SpringBootWebSecurityConfiguration配置:

@AutoConfiguration(before = {UserDetailsServiceAutoConfiguration.class}
)
@ConditionalOnClass({DefaultAuthenticationEventPublisher.class})
@EnableConfigurationProperties({SecurityProperties.class})
@Import({SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {public SecurityAutoConfiguration() {}@Bean@ConditionalOnMissingBean({AuthenticationEventPublisher.class})public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {return new DefaultAuthenticationEventPublisher(publisher);}
}

2.1 SpringBootWebSecurityConfiguration

SpringBootWebSecurityConfiguration 配置类中,默认添加了@EnableWebSecurity注解,启用了Spring Security 应用安全配置,并完成defaultSecurityFilterChain构建:

@Configuration(proxyBeanMethods = false
)
@ConditionalOnWebApplication(type = Type.SERVLET
)
class SpringBootWebSecurityConfiguration {SpringBootWebSecurityConfiguration() {}@Configuration(proxyBeanMethods = false)@ConditionalOnMissingBean(name = {"springSecurityFilterChain"})@ConditionalOnClass({EnableWebSecurity.class})@EnableWebSecuritystatic class WebSecurityEnablerConfiguration {WebSecurityEnablerConfiguration() {}}@Configuration(proxyBeanMethods = false)@ConditionalOnDefaultWebSecuritystatic class SecurityFilterChainConfiguration {SecurityFilterChainConfiguration() {}@Bean@Order(2147483642)SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((requests) -> {// 配置所有的Http请求必须认证((AuthorizeHttpRequestsConfigurer.AuthorizedUrl)requests.anyRequest()).authenticated();});// 开启表单登录认证http.formLogin(Customizer.withDefaults());// 开启basic认证http.httpBasic(Customizer.withDefaults());return (SecurityFilterChain)http.build();}}
}

2.1.1 DefaultSecurityFilterChain构建

Spring Security提供了默认实现类DefaultSecurityFilterChain,上文源码中defaultSecurityFilterChain通过HttpSecurity#build方法构建,跟踪进入HttpSecurity#performBuild源码:

在这里插入图片描述

从上面看得出HttpSecurity就是一个构建类,它的使命就是构建出一个SecurityFilterChain,查看DefaultSecurityFilterChain默认匹配所有请求,并默认存在15存在过滤器:

在这里插入图片描述

2.2 @EnableWebSecurity

@EnableWebSecurity中会导入多个配置类:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class})
@EnableGlobalAuthentication
public @interface EnableWebSecurity {boolean debug() default false;
}

下文重点介绍下WebSecurityConfiguration配置类。

2.2.1 springSecurityFilterChain构建

springSecurityFilterChainWebSecurityConfiguration中通过WebSecurity#build方法构建:

在这里插入图片描述
跟踪进入WebSecurity#performBuild源码:

	@Overrideprotected Filter performBuild() throws Exception {Assert.state(!this.securityFilterChainBuilders.isEmpty(),() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "+ "Typically this is done by exposing a SecurityFilterChain bean. "+ "More advanced users can invoke " + WebSecurity.class.getSimpleName()+ ".addSecurityFilterChainBuilder directly");int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();for (RequestMatcher ignoredRequest : this.ignoredRequests) {WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);securityFilterChains.add(securityFilterChain);requestMatcherPrivilegeEvaluatorsEntries.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));}for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();securityFilterChains.add(securityFilterChain);requestMatcherPrivilegeEvaluatorsEntries.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));}if (this.privilegeEvaluator == null) {this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(requestMatcherPrivilegeEvaluatorsEntries);}FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);if (this.httpFirewall != null) {filterChainProxy.setFirewall(this.httpFirewall);}if (this.requestRejectedHandler != null) {filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);}else if (!this.observationRegistry.isNoop()) {CompositeRequestRejectedHandler requestRejectedHandler = new CompositeRequestRejectedHandler(new ObservationMarkingRequestRejectedHandler(this.observationRegistry),new HttpStatusRequestRejectedHandler());filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);}filterChainProxy.setFilterChainDecorator(getFilterChainDecorator());filterChainProxy.afterPropertiesSet();Filter result = filterChainProxy;if (this.debugEnabled) {this.logger.warn("\n\n" + "********************************************************************\n"+ "**********        Security debugging is enabled.       *************\n"+ "**********    This may include sensitive information.  *************\n"+ "**********      Do not use in a production system!     *************\n"+ "********************************************************************\n\n");result = new DebugFilter(filterChainProxy);}this.postBuildAction.run();return result;}

springSecurityFilterChain会被FilterChainProxy代理,注册为Bean,并存放了所有的SecurityFilterChain

在这里插入图片描述
FilterChainProxy是一个GenericFilterBean(即是Servlet Filter又是Spring Bean),它管理了所有注入Spring IoC容器的SecurityFilterChain

三、UserDetailsServiceAutoConfiguration

UserDetailsServiceAutoConfiguration 只是通过加载Yml配置文件生成一个默认用户,以便于开发测试:

@AutoConfiguration
@ConditionalOnClass({AuthenticationManager.class})
@ConditionalOnBean({ObjectPostProcessor.class})
@ConditionalOnMissingBean(value = {AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class, AuthenticationManagerResolver.class},type = {"org.springframework.security.oauth2.jwt.JwtDecoder", "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector", "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository", "org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository"}
)
public class UserDetailsServiceAutoConfiguration {private static final String NOOP_PASSWORD_PREFIX = "{noop}";private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$");private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class);public UserDetailsServiceAutoConfiguration() {}@Beanpublic InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) {SecurityProperties.User user = properties.getUser();List<String> roles = user.getRoles();return new InMemoryUserDetailsManager(new UserDetails[]{User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()});}private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) {String password = user.getPassword();if (user.isPasswordGenerated()) {logger.warn(String.format("%n%nUsing generated security password: %s%n%nThis generated password is for development use only. Your security configuration must be updated before running your application in production.%n", user.getPassword()));}return encoder == null && !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password;}
}

看到这里我们就能明白解 Spring Security 6.1.x 系列(1)—— 初识Spring Security 中通过在配置文件中定义用户名和密码能生效的原因。

四、SecurityFilterAutoConfiguration

SecurityFilterAutoConfiguration自动配置类中,springSecurityFilterChain之前被声明构建过,为FilterChainProxy类型,代理了Spring Security中所有的SecurityFilterChain

因为Servlet 容器和Spring IoC容器之间的Filter生命周期并不匹配。

为了让Spring IoC容器管理Filter的生命周期,FilterChainProxy(也就是springSecurityFilterChain)便交由Spring Web下的DelegatingFilterProxy来代理。

而且FilterChainProxy不会在任何过滤器Bean上调用标准Servlet过滤器生命周期方法,FilterChainProxy的生命周期方法会委托给DelegatingFilterProxy来执行。

DelegatingFilterProxy作为Servlet容器和Spring IoC容器的生命周期之间桥接的存在。

@AutoConfiguration(after = {SecurityAutoConfiguration.class}
)
@ConditionalOnWebApplication(type = Type.SERVLET
)
@EnableConfigurationProperties({SecurityProperties.class})
@ConditionalOnClass({AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class})
public class SecurityFilterAutoConfiguration {private static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";public SecurityFilterAutoConfiguration() {}@Bean@ConditionalOnBean(name = {"springSecurityFilterChain"})public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties) {DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("springSecurityFilterChain", new ServletRegistrationBean[0]);registration.setOrder(securityProperties.getFilter().getOrder());registration.setDispatcherTypes(this.getDispatcherTypes(securityProperties));return registration;}private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {return securityProperties.getFilter().getDispatcherTypes() == null ? null : (EnumSet)securityProperties.getFilter().getDispatcherTypes().stream().map((type) -> {return DispatcherType.valueOf(type.name());}).collect(Collectors.toCollection(() -> {return EnumSet.noneOf(DispatcherType.class);}));}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LSF 守护程序和进程、集群通信路径和安全模型

LSF 细观 了解在 LSF 主机上运行的各种守护进程&#xff0c;LSF 集群通信路径&#xff0c;以及 LSF 如何容许集群中的主机故障。 1、LSF 守护程序和进程 集群中的每个主机上都运行多个 LSF 进程。 正在运行的进程的类型和数量&#xff0c;取决于主机是主节点还是计算节点。 主…

吴恩达《机器学习》2-2->2-4:代价函数

一、代价函数的概念 代价函数是在监督学习中用于评估模型的性能和帮助选择最佳模型参数的重要工具。它表示了模型的预测输出与实际目标值之间的差距&#xff0c;即建模误差。代价函数的目标是找到使建模误差最小化的模型参数。 二、代价函数的理解 训练集数据&#xff1a;假设我…

【ELFK】之Filebeat

一、Filebeat介绍 1、Filebeat是什么&#xff1f; Filebeat适用于转发和集中数据的轻量级传送工具&#xff0c;Filebeat监视了指定的日志文件或位置&#xff0c;收集日志事件&#xff0c;并将他们转发到Elasticsearch或Logstash进行索引。 **Filebeat的工作方式&#xff1a;*…

【ETL工具】Datax-ETL-SqlServerToHDFS

&#x1f984; 个人主页——&#x1f390;个人主页 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341; 感谢点赞和关注 &#xff0c;每天进步一点点&#xff01;加油&#xff01;&…

iPhone连不上Wi-Fi?看完这篇文章你就知道了!

大家在使用苹果手机的过程中有没有遇到过这样的情况&#xff1a;手机突然连接不上Wi-Fi&#xff0c;或者连接了也根本使用不了。遇到上述情况请不要着急&#xff0c;iphone连不上wifi是由很多种原因导致的。那么&#xff0c;iPhone连接不上Wi-Fi时该怎么办呢&#xff1f; 我们…

css写个三角形

点击三角形&#xff0c;展开或者收起内容 <template><div><div class"zhankai" click"btn()">展开 <span :class"{sanjiao:true,rotate:flag}"></span></div><!-- 展示或者收起 --><el-collapse-…

基于YOLOv8模型暗夜下人脸目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型暗夜下人脸目标检测系统可用于日常生活中检测与定位黑夜下人脸目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法…

大数据之路-日志采集

数据采集作为大数据体系中的第一环节&#xff0c;对如何全面、高性能、规范完成海量数据的采集&#xff0c;并将其传输到大数据平台。 1.浏览器的页面日志采集 1.1 页面浏览日志采集流程 页面浏览日志是最基础的互联网日志&#xff0c;其中页面浏览量&#xff08;PageView&am…

redis6.0源码分析:跳表skiplist

文章目录 前言什么是跳表跳表&#xff08;redis实现&#xff09;的空间复杂度相关定义 跳表&#xff08;redis实现&#xff09;相关操作创建跳表插入节点查找节点删除节点 前言 太长不看版 跳跃表是有序集合zset的底层实现之一&#xff0c; 除此之外它在 Redis 中没有其他应用。…

深度学习入门(二)之神经网络

文章目录 从感知机到神经网络神经网络的例子复习感知机激活函数 激活函数sigmoid函数阶跃函数的实现阶跃函数的图形sigmoid函数的图形sigmoid函数与阶跃函数比较非线性函数ReLU函数 多维数组的运算多维数组矩阵乘法神经网络的内积 三层神经网络的实现符号确认各层间信号传递的实…

10kb的WordPress外链页面安全跳转插件

老白博客我参照csdn和腾讯云的外链跳转页面&#xff0c;写了一个WordPress外链安全跳转插件&#xff1a;给网站所有第三方链接添加nofollow标签和重定向功能&#xff0c;提高网站安全性。插件包括两个样式&#xff0c;由于涉及到的css不太一样&#xff0c;所以分别写了两个版本…

高效处理异常值的算法:One-class SVM模型的自动化方案

一、引言 数据清洗和异常值处理在数据分析和机器学习任务中扮演着关键的角色。清洗数据可以提高数据质量&#xff0c;消除噪声和错误&#xff0c;从而确保后续分析和建模的准确性和可靠性。而异常值则可能对数据分析结果产生严重影响&#xff0c;导致误导性的结论和决策。因此&…

纪念基于JavaScript 实现的后台桌面 UI 设计

目录 前言 C/S 到 B/S ASP Builder 的诞生 关于 Craneoffice.net 开发环境配置 后台界面的 UI 区域要素 桌面系统的想法和设计 搜索引擎 导航面板 快捷访问 二级导航 小组件及其它 设置桌面壁纸 小时钟 附件小程序 计算器界面设计 日历与任务 系统设置 天气小…

【HeidiSql_01】python在heidisql当中创建新表的注意事项

python在heidisql当中创建新表的注意事项 假设你已经在python当中弄好了所有的结果&#xff0c;并且保存在df_all这个dataframe当中&#xff0c;然后要将其导入数据库当中并创建一张新的表进行保存。 # 构建数据库连接,将merged_df写回数据库 from sqlalchemy import create_e…

进口跨境商城源码:高效、安全、可扩展的电商平台解决方案

电子商务的兴起为跨境贸易提供了前所未有的机会和挑战。在这个全球化的时代&#xff0c;跨境电商平台成为许多企业进军国际市场的首选。然而&#xff0c;搭建一个高效、安全、可扩展的进口跨境商城并非易事。 1. 解决方案概述 我们推出的 "进口跨境商城源码" 提供了一…

小程序商城免费搭建之java商城 电子商务Spring Cloud+Spring Boot+二次开发+mybatis+MQ+VR全景+b2b2c

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

uniapp @click点击事件在新版chrome浏览器点击没反应

问题描述 做项目时&#xff0c;有一个弹出选择的组件&#xff0c;怎么点都不出来&#xff0c;最开始还以为是业务逻辑限制了不能点击。后来才发现别人的电脑可以点出来&#xff0c;老版本的浏览器也可以点出来&#xff0c;最后定位到是新版的chrome就不行了 这是我的浏览器版本…

【C++的OpenCV】第十四课-OpenCV基础强化(三):单通道Mat元素的访问之data和step属性

&#x1f389;&#x1f389;&#x1f389; 欢迎来到小白 p i a o 的学习空间&#xff01; \color{red}{欢迎来到小白piao的学习空间&#xff01;} 欢迎来到小白piao的学习空间&#xff01;&#x1f389;&#x1f389;&#x1f389; &#x1f496; C\Python所有的入门技术皆在 我…

STM32F407的系统定时器

文章目录 系统定时器SysTick滴答定时器寄存器STK_CTRL 控制寄存器STK_LOAD 重载寄存器STK_VAL 当前值寄存器STK_CALRB 校准值寄存器 初始化 Systick 定时器SysTick_InitSysTick_CLKSourceConfig delay_us寄存器delay_us库函数delay_xms短时delay_ms长时SysTick_Config 系统定时…

HTML和CSS的基础-前端扫盲

想要写出一个网页&#xff0c;就需要学习前端开发&#xff08;写网页代码&#xff09;和后端开发&#xff08;服务器代码&#xff09;。 对于前端的要求&#xff0c;我们不需要了解很深&#xff0c;仅仅需要做到扫盲的程度就可以了。 写前端&#xff0c;主要用到的有&#xf…