【个人版】SpringBoot下Spring-Security核心概念解读【二】

news2024/11/29 10:50:18

Spring-Security + HttpSecurity

Spring-Security全局导读:
1、Security核心类设计
2、HttpSecurity结构和执行流程解读
3、Spring-Security个人落地篇

背景: Spring-Security框架的核心架构上一篇已经概述,展示其执行流程及逻辑,但是和我们实际使用有点差距,相信大家在使用此框架时,肯定被以下代码迷惑过:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .formLogin().disable()
                .authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll()
                .anyRequest().authenticated()
                .and().logout().logoutSuccessHandler(authResult).permitAll()
                .and().exceptionHandling().authenticationEntryPoint(authResult);
    }
}

上述代码直接看就是一串httpSecurity对象的链式配置,但很多问题没有还需明确
1、HttpSecurity可以设置哪些配置点? 重点配置有哪些?
2、链条中为什么需要add方法?是否必须?
3、每个配置点底层以何种形式存在?执行时机或顺序如何定义?
4、HttpSecurity除了链式配置外,还需要哪些配置?
5、在适配模式下使用,重写的configure方法何时执行?
6、HttpSecurity在何处以何种方式被使用的?

下面将在以上问题的基础上,对HttpSecurity做自我理解后的解读:
基础条件:
1、SpringBoot的Web项目【版本2.7.14】
2、集成相关spring-security的starter依赖

解答一、HttpSecurity的由来
上下文自动配置类:
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration

引入Security注解开关:
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
传递关系(依次由上到下):

  • SecurityAutoConfiguration
  • SpringBootWebSecurityConfiguration
  • WebSecurityEnablerConfiguration
  • EnableWebSecurity

进而引入Security框架核心类的配置类:
AuthenticationConfiguration => AuthenticationManager管理类
WebSecurityConfiguration => WebSecurity配置类
HttpSecurityConfiguration => HttpSecurity配置类

HttpSecurity创建代码简化版:

@Configuration(
    proxyBeanMethods = false
)
class HttpSecurityConfiguration {
    private ObjectPostProcessor<Object> objectPostProcessor; // 框架类初始化增强类,后续单独讲
    private AuthenticationManager authenticationManager; // 授权管理器,核心依赖类,可自定义,可系统自动配置,后续单独讲
    private AuthenticationConfiguration authenticationConfiguration; // 授权管理器配置类,作为父授权管理器托底
    private ApplicationContext context;

	@Scope("prototype")
    @Bean({"org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity"})
    HttpSecurity httpSecurity() throws Exception {
        WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder = new WebSecurityConfigurerAdapter.LazyPasswordEncoder(this.context);
        // 创建授权管理器构建类,parent设置时可以全局搜索我们自定义的AuthenticationProvider类,就不用通过构建器手工设置
		AuthenticationManagerBuilder authenticationBuilder = new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(this.objectPostProcessor, passwordEncoder);
        authenticationBuilder.parentAuthenticationManager(this.authenticationManager());
		// 实例化HttpSecurity
        HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, this.createSharedObjects());
        // 初始化HttpSecurity安全管理逻辑,如logout、登录页、异常等场景设置,都是默认配置,做到开箱即用
		http.csrf(Customizer.withDefaults()).addFilter(new WebAsyncManagerIntegrationFilter()).exceptionHandling(Customizer.withDefaults()).headers(Customizer.withDefaults()).sessionManagement(Customizer.withDefaults()).securityContext(Customizer.withDefaults()).requestCache(Customizer.withDefaults()).anonymous(Customizer.withDefaults()).servletApi(Customizer.withDefaults()).apply(new DefaultLoginPageConfigurer());
        http.logout(Customizer.withDefaults());        
		this.applyDefaultConfigurers(http); // SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader) -- 默认factories加载实现类,忽略	
        return http;
    }

    private AuthenticationManager authenticationManager() throws Exception {
        return this.authenticationManager != null ? this.authenticationManager : this.authenticationConfiguration.getAuthenticationManager();
    }

	// HttpSecurity内部全局共享实例集合,核心类都会在此保存并重复使用
    private Map<Class<?>, Object> createSharedObjects() {
        Map<Class<?>, Object> sharedObjects = new HashMap();
        sharedObjects.put(ApplicationContext.class, this.context);
        return sharedObjects;
    }
}

除代码中标注的注释外,还需要注意:
1、注意HttpSecurity上@Bean发布范围是多例,因为可以Proxy下可以配置多个过滤器链(注:如果在不同类或同一个类的不同地方多次引用,相关配置会错乱,除非自己另行全局封装)
2、AuthenticationManager是部分Filter的核心依赖,尤其涉及我们自定义鉴权部分的代码,其主旨为拦截后对接口具体的参数处理与返回,也就是我们的鉴权逻辑。

解答二:HttpSecurity结构之配置与构建过程
作为一个框架的核心类,HttpSecurity的设计完成应用了多个巧妙的设计模式,使代码可读性、复用性更高。
核心设计模式一:构建器模式
构建器模式是最常见的创建型模式,在类相关实例变量相当多的场景下非常实用,避免长列表或多重载构造函数。在Security框架中,安全策略多样,每个安全策略需要管理的参数量不固定且灵活性较高(隐含意思是:HttpSecurity配置安全策略及安全策略配置具体参数都适用此场景,实际security框架就是这么设计和干的),如果采用常见if…else…语法,配置及使用过程将毫无人性,构建器模式让HttpSecurity的链式配置更加易读和使用。

Spring6中将配置器的创建改成lambda形式,进一步缩短HttpSecurity的配置层次,将配置器的创建与HttpSecurity的配置解耦,这就是框架的魅力。

构建器模式代码结构很简单,难点在于安全知识点的理解与调控,开发过程中注意参数变化导致的配置器的失效问题,下面将简单列举来进一步说明:
在这里插入图片描述
1、图中1处的logout无参方法返回配置器Configurer的实例对象,我们通过配置内部构建方法进一步配置其他参数值,结束配置可用HttpSecurity另起一行配置新的配置器,或者使用logout配置器的and方法继续链式配置其他配置器
2、图中2处的logout有参方法提供了一个自定义构造器参数,可以对系统默认的Configurer实例做进一步配置,可对象可由lambda表达式代替,Spring6已经全面像此场景靠拢(部分人对过长的HttpSecurity链无感)

配置器操作中其实隐含一个非常重要的操作,可以看到图中的getOrApply方法。该方法一方面将Configurer实例添加到HttpSecurity的全局缓存中,并将HttpSecurity的实例对象的引用传递进原Configurer内部,做到相交相融,还有将Configurer从普通new出的实例进一步提级,完成类似spring容器里的实例对象的生命周期的管理。根据反馈效果,后续可追加解读。

核心设计模式二:模板模式
模板模式常用在继承结构,HttpSecurity继承了AbstractConfiguredSecurityBuilder -> AbstractSecurityBuilder抽象类,该抽象类将构建方法完全委托给实现类完成,已知实现类有:HttpSecurity、WebSecurity、AuthenticationManagerBuilder,可以看到这三个类正是Security框架的核心。模板模式特点之xxx -> doXxx,构建过程如下:

protected final O doBuild() throws Exception {
    synchronized(this.configurers) {
        this.buildState = AbstractConfiguredSecurityBuilder.BuildState.INITIALIZING;
        this.beforeInit();
        this.init();
        this.buildState = AbstractConfiguredSecurityBuilder.BuildState.CONFIGURING;
        this.beforeConfigure();
        this.configure();
        this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILDING;
        O result = this.performBuild();
        this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILT;
        return result;
    }
}

构建流程说明:
1、beforeInit()

  • 尴尬,三个实现类都忽略了

2、init()

  • 执行所有安全配置器的初始化代码,即configurer.init(HttpSecurity)

3、beforeConfigure()

  • 授权管理器实例化并添加到sharedObjects集合,AuthenticationManagerBuilder在此构建后状态标记已变更,不可重新变更,只能获取并使用。构建后的实例,可以注入到HttpSecurity中可以创建Filter的配置器中,自定义安全Filter需要人工或特殊配置来设置。(这块有一定的操作空间)

4、configure()

  • 这个方法和init方法相辅相成,前面已经完成各配置器的初始化,现完成调用各配置器Configurer的configure方法,逐一完成对HttpSecurity的配置。(此块即完成security框架内部Filter的添加,例:LogoutFilter)

5、performBuild()

  • HttpSecurity构建的结果就是SecurityFilterChain对象,此对象的功能就是用来管理security内部Filter集合,创建逻辑也很简单:new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters)。关于SecurityFilterChain的介绍,请点击并移步至此篇。

概要说明:
1、构建流程的入口是我们HttpSecurity.build方法,该方法可由我们主动触发,在WebSecurityConfigurerAdapter适配器模式下,由框架触发
2、授权管理器用来鉴权逻辑,所以一般配置器生产的Filter实例可不用配置,必要的Filter也会做检查。授权管理器对应的构建器在代码里有一定的操作空间,其构建状态影响较大。
3、添加到HttpSecurity的内部安全Filter有很多,这些Filter不影响我们正常使用的ApplicationFilterChain的执行流程,各Filter在集合中的顺序有默认顺序(详见FilterOrderRegistration,包含所有内部过滤器清单及Order),自定义Filter可通过HttpSecurity.addFilterBefore/After等方法指定。

总结:
通过上述两大设计模式对Security框架的解读,文章开篇的几个问题基本都涵盖了,自己可以对照着解读,根据问题和自己的理解去源码中进一步验证,再来一遍debug,基本就可以结束了。下面文章将根据这两篇前缀,直接出一个完整的个人版落地篇,也就是我们实际使用这个框架的内容了。

PS:还有一个重要流程其实没有阐述,就是上一篇的FilterChainProxy与本篇的HttpSecurity构建的SecurityFilterChain对象是如何绑定的,其答案为:WebSecurity通过控制HttpSecurity的配置与构建过程生成SecurityFilterChain,在对应构建方法中使用FilterChainProxy包装,进而发布到外部ApplicationFilterChain过滤器链中。如果感兴趣,以后有机会再开篇详解。

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

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

相关文章

设计可编辑表格组件

前言 什么是可编辑表格呢&#xff1f;简单来说就是在一个表格里面进行表单操作&#xff0c;执行增删改查。这在一些后台管理系统中是尤为常见的。 今天我们根据vue2 element-ui来设计一个表单表格组件。&#xff08;不涉及完整代码&#xff0c;想要使用完整功能可以看底部连…

推荐EasyImages简单图床源码

开源好用EasyImages简单图床源码分享&#xff0c;虽然它是开源程序&#xff0c;但功能一点也不弱&#xff0c;不仅支持多文件上传、文字/图片水印、支持API和鉴黄、还能自定义代码&#xff0c;最重要的是它不强制使用数据库运行&#xff0c;这就给我们的部署和维护带来极大方便…

21--集合小案例

案例--图书管理系统 1.创建实体类Book package com.work.pojo; /** *Author: 憨憨浩浩 *CreateTime: 2023-12-16 17:27 *Description: Book实体类 */ public class Book {private int id; // 编号private String name; // 图书名称private String author;…

Stable-Diffusion|文生图 完蛋我被美女包围了人物Lora(四)

前面几篇&#xff1a; Stable-Diffusion|window10安装GPU版本的 Stable-Diffusion-WebUI遇到的一些问题&#xff08;一&#xff09; 【Stable-Diffusion|入门怎么下载与使用civitai网站的模型&#xff08;二&#xff09;】 Stable-Diffusion|文生图 拍立得纪实风格的Lora 图例&…

【JavaEE】多线程案例 - 定时器

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文于《JavaEE》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力)打造&…

漏洞复现-海康威视 NCG 联网网关 login.php 目录遍历漏漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

新增数据,某个字段的值总是保存不上问题解决

在系统中新增一条数据&#xff0c;某个字段的数据总是保存不上&#xff0c;但是没有报任何异常和错误&#xff0c;其他字段也都是正常的&#xff0c;通过抓包分析请求参数发现那个字段的值也没有传给后端&#xff0c;检查了前后端代码也没有排查到问题。百思不得其解&#xff0…

【5G PHY】5G小区类型、小区组和小区节点的概念介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

深入理解——快速排序

目录 &#x1f4a1;基本思想 &#x1f4a1;基本框架 &#x1f4a1;分割方法 ⭐Hoare版本 ⭐挖坑法 ⭐前后指针法 &#x1f4a1;优化方法 ⭐三数取中法 ⭐小区间内使用插入排序 &#x1f4a1;非递归实现快速排序 &#x1f4a1;性能分析 &#x1f4a1;基本思想 任取待排…

Java已死!

许多开发者仍然认为 Java 与当今时代息息相关&#xff0c;看完本文&#xff0c;你会发现 Java 的影响力已经大幅减弱。实际上&#xff0c;Java 是一种濒临灭绝的编程语言。尽管 Java 一直是世界上使用最广泛、最受欢迎的编程语言之一&#xff0c;但它很快就会面临消亡的危险。 …

【OpenHarmony 北向应用开发】ArkTS语言入门(构建应用页面)

ArkTS语言入门 在学习ArkTS语言之前&#xff0c;我们首先需要一个能够编译并运行该语言的工具 DevEco Studio。 了解ArkTS ArkTS是OpenHarmony优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript&#xff08;简称TS&#xff09;生态基础上做了进一步扩展&#xff0c;继…

01--二分查找

一. 初识算法 1.1 什么是算法&#xff1f; 在数学和计算机科学领域&#xff0c;算法是一系列有限的严谨指令&#xff0c;通常用于解决一类特定问题或执行计算 不正式的说&#xff0c;算法就是任何定义优良的计算过程&#xff1a;接收一些值作为输入&#xff0c;在有限的时间…

【动态读取配置文件】ParameterTool读取带环境的配置信息

不同环境Flink配置信息是不同的&#xff0c;为了区分不同环境的配置文件&#xff0c;使用ParameterTool工具读取带有环境的配置文件信息 区分环境的配置文件 三个配置文件&#xff1a; flink.properties&#xff1a;决定那个配置文件生效 flink-dev.properties&#xff1a;测…

【计算机网络】—— 详解码元,传输速率的计算|网络奇缘系列|计算机网络

&#x1f308;个人主页: Aileen_0v0&#x1f525;系列专栏: 一见倾心,再见倾城 --- 计算机网络~&#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 码元 速率和波特 思考1 思考2 思考3 带宽&#xff08;Bandwidth&#xff09; &#x1f4dd;总结 码元…

MapReduce序列化实例代码

1 &#xff09;需求&#xff1a;统计每个学号该月的超市消费、食堂消费、总消费 2 &#xff09;输入数据格式 序号 学号 超市消费 食堂消费 18 202200153105 8.78 12 3 &#xff09;期望输出格式 key &#xff08;学号&#xff09; value &#xff08; bean 对象&#xf…

用Rust刷LeetCode之58 最后一个单词的长度

58. 最后一个单词的长度[1] 难度: 简单 原描述: 思路 使用标准库: package mainimport ( "fmt" "strings")func lengthOfLastWord(s string) int { s strings.TrimSpace(s) // 删除 首尾 的空格 arr : strings.Split(s, " ") // 字符串转为切片…

FL Studio 21.1.0.3713中文版最新安装激活图文教程及系统配置要求

FL Studio 21.1.0.3713中文版是一款功能强大的编曲软件&#xff0c;它也能够剪辑、混音、录音&#xff0c;它的矢量界面&#xff0c;能更好用在4K、5K甚至8K显示器上。完全重新设计混音器、动态缩放、具有 6 种布局风格、外加 3个用户自定义面板管理音轨、多推子选择和调整、混…

Python 反编译Il2Cpp APK

引入 https://github.com/Perfare/Il2CppDumper/ 实现 开源的Ii2Cpp Dumper可以帮助我们将So和globalmetadata.dat文件反编译出 Assembly-CSharp.dll 本博客教程可以帮助我们直接拖入APK反编译出来 调用方式 两种 第一种 拖入后回车运行 第二种 放入运行的根目录下 源码 i…

Pandas-DataFtame的索引与切片(第3讲)

Pandas-DataFtame的索引与切片(第3讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…

数据解密战:.mallox勒索病毒攻击下的数据保护

引言&#xff1a; 近期&#xff0c;网络安全领域再度掀起一场不安的浪潮&#xff0c;.Mallox勒索病毒作为最新一位悍匪登场&#xff0c;以其毒辣的加密技术&#xff0c;将用户的数据转瞬间变成了无解之谜。这股数字黑暗力量的出现引起了广泛关注&#xff0c;让用户和企业重新审…