【Spring Security】—— WebSecurityConfigurerAdapter

2024-03-10 22:30

本文主要是介绍【Spring Security】—— WebSecurityConfigurerAdapter,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

官网地址 Spring Security Reference

版本:Version 5.5.0

WebSecurityConfigurerAdapter 关系图

Adapter 谷歌翻译:n. 【机】转接器 【网络】 适配器;适配器模式;接头。通过类名了解功能:我的理解,这个类是一个Web应用安全配置“接头”,及用户可通过这个“接头”接到自己的配置,也就是用户可以利用这个类来定制化安全配置。
在这里插入图片描述

SecurityBuilder 接口

对这个接口暂时没有细看,先根据官网了解了一下这个接口想要实现的功能。不想扣的太细把自己绕进去,后面再回头细看。

public interface SecurityBuilder<O> {O build() throws Exception;
}
  • 接口功能描述:

    Interface for building an Object

    构建一个对象,泛型参数 O 代表将要构建的对象类型。

  • 待实现方法:

    O build();
    

    该接口只有这一个待实现的方法,该方法返回一个要构造的对象(类型为 O )或者 null。

    也就是说,SecurityBuilder的实现类的作用就是构建一个对象,至于怎么构建由具体实现完成,至于构造什么对象由传入的泛型决定。

SecurityConfigurer 接口

对这个接口暂时没有细看,先根据官网了解了一下这个接口想要实现的功能。不想扣的太细把自己绕进去,后面再回头细看。

public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {/*** 初始化 SecurityBuilder。* 这里只应该创建和修改共享状态,而不是用于构建对象的 SecurityBuilder 上的属性。 * 这里需要确保 configure(SecurityBuilder) 方法在执行时使用正确的共享对象。 */void init(B var1) throws Exception;/*** 通过在 SecurityBuilder 上设置必要的属性来配置 SecurityBuilder。*/void configure(B var1) throws Exception;
}
  • 官网对这个接口的描述:

    Allows for configuring a SecurityBuilder. All SecurityConfigurer first have their init(SecurityBuilder) method invoked. After all init(SecurityBuilder) methods have been invoked, each configure(SecurityBuilder) method is invoked.
    Type Parameters:
    1. O - The object being built by the SecurityBuilder B
    2. B - The SecurityBuilder that builds objects of type O. This is also the SecurityBuilder that is being configured.

    翻译过来:SecurityConfigurer 可以配置 SecurityBuilder。 所有 SecurityConfigurer 首先调用它们的 init(SecurityBuilder) 方法。 在调用了所有 init(SecurityBuilder) 方法之后,将调用每个 configure(SecurityBuilder) 方法。
    类型上到泛型参数:
    1. O :SecurityBuilder B 正在构建的对象
    2. B :构建O对象实例的构建器,这个构建器正在被配置。

  • 个人理解:
    从官网的描述,大概可以推测:web容器中可以有多个 SecurityConfigurer ,每个 SecurityConfigurer 可以配置 SecurityBuilder,而 SecurityBuilder 的作用时构造一个对象,那么逻辑上感觉可能是这样的:
    在这里插入图片描述
    SecurityConfigurer 和 SecurityBuilder 的最终目的时构造出一个对象 O ,而构造的过程由 SecurityBuilder 完成,SecurityConfigurer 可以配置SecurityBuilder 的具体构建细节。

第一眼看到SecurityConfigurer时产生了一个疑问:SecurityConfigurer<O, B extends SecurityBuilder<O>> 这个结构看上去和 Map<K,V>有点类似,那SecurityConfigurer是不是也可以实现成类似Map的结构,可以键值对应?

WebSecurityConfigurer 接口
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends SecurityConfigurer<Filter, T> {}

该接口继承了 SecurityConfigurer 接口,从源码中可以看出,它没有定义自己的方法,所有的方法都从父接口继承(init 和 configure 两个方法),那么他的作用本质上还是配置构造器SecurityBuilder,但是它对泛型做了约束:

这里涉及到的泛型:

  1. 将要输入WebSecurityConfigurer 的泛型 T
    T 继承了SecurityBuilder,所有 T 表示一个对象构建器(SecurityBuilder)
  2. 传入 SecurityBuilder 的泛型 Filter(javax.servlet.Filter)
    表示 SecurityBuilder 将要创建的对象是一个 javax.servlet.Filter ,也就是说,它约束了 T , 说明 T 的作用是构建 Filter
  3. 将要传入父接口SecurityConfigurer的两个泛型: Filter 和 T
    WebSecurityConfigurer 继承 SecurityConfigurer,从这里可以看出,对SecurityConfigurer做了约束,目前只有 T 还没有指定具体的类型,至于最终使用什么类型的构建器由实现类后者子接口器指定。

在SpringSecurity框架中,WebSecurityConfigurer 接口只提供了一个实现抽象类 WebSecurityConfigurerAdapter,该实现类指定的对象构建器是 WebSecurity,改对象构建器将来构建的对象是 Filter (过滤器):

public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>implements SecurityBuilder<Filter>, ApplicationContextAware { " 省略源码 " }

WebSecurityConfigurerAdapter 抽象类

总体理解
public abstract class WebSecurityConfigurerAdapter implementsWebSecurityConfigurer<WebSecurity> {"省略具体代码"}

官网描述:

Provides a convenient base class for creating a WebSecurityConfigurer instance. The implementation allows customization by overriding methods.
为创建 WebSecurityConfigurer 实例提供方便的基类。 该实现允许通过覆盖方法进行定制

这个类是我使用SpringSecurity框架时接触的第一个类,通过继承这个类,再覆盖其中的config方法,做一些定制,再加上@EnableWebSecurity注解,一个简单的安全认证服务就集成好了。
在这里插入图片描述
这是源码的另一部分注解,这部分我不知道怎么直接翻译,总觉得说出来意思有点绕。这一段的意思主要就是说:允许开发人员扩展默认的配置 allow developers to extend the defaults

  • 扩展方式:
    通过在 META-INF/spring.factories 文件中添加自己的配置,然后会由SpringFactoriesLoader 自动加载到内存,然后适配到SpringSecurity框架中。

    org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer #多个值用逗号隔开
    

    也就是说,我们通过继承 AbstractHttpConfigurer 定制化,然后配置到spring.factories,SpringSecurity框架会自动装配。

  • AbstractHttpConfigurer 是什么?
    一组安全配置器。这里我不打算详细的看这块代码,我怕自己被绕晕。我看来一下它的解释,它的目的是构建一个对HTTP请求进行安全控制的DefaultSecurityFilterChain。这样的化就很好理解了,也就是手我们可以自定义FilterChain来扩展默认的。

详细了解

通过idea工具可以看到 WebSecurityConfigurerAdapter 中都有些什么:

  • 四个内部类
    内部类
  • 一堆成员变量和方法,其中有两个构造方法,
    成员变量和方法列表
内部类
  1. LazyPasswordEncoder
    处理密码的,比如对密码进行比对,对用户输入加密等
  2. UserDetailsServiceDelegator
    处理用户详细信息的服务代理,获取用户的详细信息的代理,里面有三种方式,内存中的用户信息,JDBC连接获取用户信息,serviceDao来查询用户信息
  3. AuthenticationManagerDelegator
    认证管理器代理类,利用认证管理器镜像用户认证
  4. DefaultPasswordEncoderAuthenticationManagerBuilder
    默认认证管理器构建器,构建认证管理器的
方法

先看构造方法。因为在看 SecurityConfigurer 接口时知道,它会先执行init方法初始化构建器,再执行config方法配置构建器,WebSecurityConfigurerAdapter 本质上就是实现 SecurityConfigurer 接口,所有我接下按这个顺序看源码。

  1. 构造方法
    在这里插入图片描述

    protected WebSecurityConfigurerAdapter() {this(false);
    }protected WebSecurityConfigurerAdapter(boolean disableDefaults) {this.disableDefaults = disableDefaults;
    }
    

    无参构造函数内部直接调用有参构造函数并传入false参数。disableDefaults 参数可以指定是否禁用默认配置,如果要禁用,必须对整改框架的实现足够了解。

  2. init 方法

    根据 SecurityConfigurer 接口中对init方法的描述,这里应该只是对 SecurityBuilder(在这里指的对象构建器就是WebSecurity,将要构建的对象就是Filter对象) 的一些状态进行了创建设置等,并没有进行正在的配置,配置工作最终交由config方法完成。

    //Initialize the SecurityBuilder.
    public void init(final WebSecurity web) throws Exception {// 获取一个 HttpSecurity 对象实例final HttpSecurity http = getHttp();// 将 http 放入 web 并追加 postBuildActionweb.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {public void run() {FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);web.securityInterceptor(securityInterceptor);}});
    }
    

    方法只有一个参数:WebSecurity 类型的一个对象(这与所实现接口中指定的哪个泛型是一致的,作用是用来构建Filter对象),这个参数对象被 final 修改,表示在方法执行过程中不可变(即不可以在方法中通过赋值的方式改变它),就是与所实现接口泛型指定的 WebSecurity 类型的一个对象。

    init方法中做了三件事:

    • 调用 getHttp 方法获得了一个 HttpSecurity 实例对象 http,获取到的这个实例被 final 修饰,也就是说 http 被创建出来之后就不可以再通过赋值的方式改变。

      点开 HttpSecurity 源码简单看来一下定义和注释,发现它也实现了 SecurityBuilder 接口,接口指定的泛型是 DefaultSecurityFilterChain ,那么它也是一个对象构建器,它可以根据具体的配置构建出一个过滤器链,最终对到达应用程序的http请求进行过滤,为服务提供 http 安全保护。

    • 将 HttpSecurity 放入 WebSecurity 中
      点开 addSecurityFilterChainBuilder 方法看了一下:

      private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();public WebSecurity addSecurityFilterChainBuilder( SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {this.securityFilterChainBuilders.add(securityFilterChainBuilder);return this;
      }
      

      把刚才获取到的 http 添加到WebSecurity 的 securityFilterChainBuilders 属性中,securityFilterChainBuilders 本质上是一个 ArrayList 。这个属性在执行build的时候会用到。

    • 为WebSecurity 追加了一个postBuildAction
      postBuildAction 从字面猜测它的功能是在build之后的action,也就是在 WebSecurity 完成构建对象任务后立即执行的后置操作,这里的后置操作通过一个Runnable另启了一个线程来完成:在 build 都完成后从 http 中拿出 FilterSecurityInterceptor 类型的共享对象 并赋值给WebSecurity。

      FilterSecurityInterceptor 本质就是 Filter,这说明在 getHttp 方法中已经准备好了这个共享对象。

  3. configure 方法
    在这里插入图片描述

    // 默认方法实现中只是设置了 disableLocalConfigureAuthenticationBldr ,以判断怎么获取 AuthenticationManager
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {this.disableLocalConfigureAuthenticationBldr = true;
    }public void configure(WebSecurity web) throws Exception {
    }protected void configure(HttpSecurity http) throws Exception {logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
    }
    

    有三个 configure 方法:

    1. configure​(AuthenticationManagerBuilder auth)
      官网注释:由 authenticationManager() 的默认实现调用,以尝试获取 AuthenticationManager。
      点进 authenticationManager()方法看一下:

      	protected AuthenticationManager authenticationManager() throws Exception {if (!authenticationManagerInitialized) {//调用 configure(AuthenticationManagerBuilder auth) 方法,默认情况下只是将disableLocalConfigureAuthenticationBldr 属性设置为 trueconfigure(localConfigureAuthenticationBldr);// 根据 disableLocalConfigureAuthenticationBldr 的值选择获取 AuthenticationManager 的方式if (disableLocalConfigureAuthenticationBldr) {authenticationManager = authenticationConfiguration.getAuthenticationManager();} else {authenticationManager = localConfigureAuthenticationBldr.build();}authenticationManagerInitialized = true;}return authenticationManager;}
      

      看到这里就产生了疑问,authenticationConfiguration 和 localConfigureAuthenticationBldr 是哪里来的 ?这里涉及到了另外两个属性设置方法:

      // 自动注入的 AuthenticationConfiguration 
      @Autowired
      public void setAuthenticationConfiguration(AuthenticationConfiguration authenticationConfiguration) {this.authenticationConfiguration = authenticationConfiguration;
      }
      // 自动注入 ApplicationContext ,并在这里完成一些设置
      @Autowired
      public void setApplicationContext(ApplicationContext context) {this.context = context;// 从上下文中获取后置处理器ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);// 设置 authenticationBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);// 设置 localConfigureAuthenticationBldr localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder) {@Overridepublic AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {authenticationBuilder.eraseCredentials(eraseCredentials);return super.eraseCredentials(eraseCredentials);}};
      }
      

      在这两个方法中设置了 authenticationConfiguration 和 localConfigureAuthenticationBldr,这里需要说明一下 authenticationConfiguration 的来源:
      默认情况下应该是框架默认会在上下文中设置一个默认的AuthenticationConfiguration类型的Bean,这样才能自动注入进来。如果不使用默认的,应该可以覆盖这个config方法配置AuthenticationManagerBuilder 构建器,然后在authenticationManager()调用这个config方法时,就可以通过定制的AuthenticationManagerBuilder来构建 AuthenticationConfiguration。

      authenticationManager()方法回在 getHttp()方法中被触发。源码注解中也给出了覆盖这个方法的例子:
      在这里插入图片描述

    2. configure​(WebSecurity web)
      覆盖此方法以配置 WebSecurity。默认是一个空的方法体。这个方法是一个pubilc方法。

    3. configure​(HttpSecurity http)
      覆盖此方法以配置 HttpSecurity。默认情况下会追加一些 http 配置。

  4. getHttp 方法

    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected final HttpSecurity getHttp() throws Exception {if (http != null) {return http;}DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);// 调用 authenticationManager() 方法,试图获取 AuthenticationManager AuthenticationManager authenticationManager = authenticationManager();authenticationBuilder.parentAuthenticationManager(authenticationManager);authenticationBuilder.authenticationEventPublisher(eventPublisher);// 共享对象,类是缓存的功能,存储配置中共享数据Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();// 创建 HttpSecurity 对象,将准备好的配置都放进去http = new HttpSecurity(objectPostProcessor, authenticationBuilder,sharedObjects);// 调用默认构造方法创建WebSecurityConfigurerAdapter时,默认http配置是开启的,即默认 disableDefaults 为 falseif (!disableDefaults) {// @formatter:offhttp.csrf().and().addFilter(new WebAsyncManagerIntegrationFilter()).exceptionHandling().and().headers().and().sessionManagement().and().securityContext().and().requestCache().and().anonymous().and().servletApi().and().apply(new DefaultLoginPageConfigurer<>()).and().logout();// @formatter:on// 获取类加载器ClassLoader classLoader = this.context.getClassLoader();/*** 根据 spring.factories 文件中配置的 AbstractHttpConfigurer (安全配置器)* 这个以之前官网中所提到的扩展方式相对应*/List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);/*** 将加载的 AbstractHttpConfigurer 适配到 HttpSecurity 构建器的 configurers 中* 用途就是 HttpSecurity 会用这些配置来构建 Filter* 		HttpSecurity 构建器的 configurers 是一个集合,用来存储配置对象的*/for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {http.apply(configurer);}}// 调用 configure(HttpSecurity http) 方法,追加自定义配置configure(http);return http;
    }
    

    getHttp 方法在 init 方法中就被触发了,为后面构建对象做准备。这里只是大概看来已选http.apply方法,关于 HttpSecurity 中的细节后续在详细了解。

  5. authenticationManagerBean方法

    Override this method to expose the AuthenticationManager from configure(AuthenticationManagerBuilder) to be exposed as a Bean.
    覆盖此方法将 AuthenticationManager 对象作为一个Bean 公开,从 configure(AuthenticationManagerBuilder)方法 。

    public AuthenticationManager authenticationManagerBean() throws Exception {return new AuthenticationManagerDelegator(authenticationBuilder, context);
    }
    

    这个方法在官网描述的功能是:将一个 AuthenticationManager 对象以作为 Bean 公开。这样就可以在继承WebSecurityConfigurerAdapter的配置类中通过重写这个方法并将返回值公开为 Bean,注入我们自己实现的 AuthenticationManager ,官方的例子:
    在这里插入图片描述

  6. 剩余其他方法
    剩下的方法是一些属性配置的方法,可以对适配器进行属性的 set 配置。还有关于用户详细信息服务(UserDetailsService)的方法,也比较好理解: 在这里插入图片描述

这篇关于【Spring Security】—— WebSecurityConfigurerAdapter的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/795718

相关文章

springboot集成easypoi导出word换行处理过程

《springboot集成easypoi导出word换行处理过程》SpringBoot集成Easypoi导出Word时,换行符n失效显示为空格,解决方法包括生成段落或替换模板中n为回车,同时需确... 目录项目场景问题描述解决方案第一种:生成段落的方式第二种:替换模板的情况,换行符替换成回车总结项目场景s

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

SpringBoot中@Value注入静态变量方式

《SpringBoot中@Value注入静态变量方式》SpringBoot中静态变量无法直接用@Value注入,需通过setter方法,@Value(${})从属性文件获取值,@Value(#{})用... 目录项目场景解决方案注解说明1、@Value("${}")使用示例2、@Value("#{}"php

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏