【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

相关文章

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

SpringCloud集成AlloyDB的示例代码

《SpringCloud集成AlloyDB的示例代码》AlloyDB是GoogleCloud提供的一种高度可扩展、强性能的关系型数据库服务,它兼容PostgreSQL,并提供了更快的查询性能... 目录1.AlloyDBjavascript是什么?AlloyDB 的工作原理2.搭建测试环境3.代码工程1.

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

SpringBoot操作spark处理hdfs文件的操作方法

《SpringBoot操作spark处理hdfs文件的操作方法》本文介绍了如何使用SpringBoot操作Spark处理HDFS文件,包括导入依赖、配置Spark信息、编写Controller和Ser... 目录SpringBoot操作spark处理hdfs文件1、导入依赖2、配置spark信息3、cont

springboot整合 xxl-job及使用步骤

《springboot整合xxl-job及使用步骤》XXL-JOB是一个分布式任务调度平台,用于解决分布式系统中的任务调度和管理问题,文章详细介绍了XXL-JOB的架构,包括调度中心、执行器和Web... 目录一、xxl-job是什么二、使用步骤1. 下载并运行管理端代码2. 访问管理页面,确认是否启动成功

Java中的密码加密方式

《Java中的密码加密方式》文章介绍了Java中使用MD5算法对密码进行加密的方法,以及如何通过加盐和多重加密来提高密码的安全性,MD5是一种不可逆的哈希算法,适合用于存储密码,因为其输出的摘要长度固... 目录Java的密码加密方式密码加密一般的应用方式是总结Java的密码加密方式密码加密【这里采用的

Java中ArrayList的8种浅拷贝方式示例代码

《Java中ArrayList的8种浅拷贝方式示例代码》:本文主要介绍Java中ArrayList的8种浅拷贝方式的相关资料,讲解了Java中ArrayList的浅拷贝概念,并详细分享了八种实现浅... 目录引言什么是浅拷贝?ArrayList 浅拷贝的重要性方法一:使用构造函数方法二:使用 addAll(

解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题

《解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题》本文主要讲述了在使用MyBatis和MyBatis-Plus时遇到的绑定异常... 目录myBATis-plus-boot-starpythonter与mybatis-spring-b