【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

相关文章

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

SpringBoot整合liteflow的详细过程

《SpringBoot整合liteflow的详细过程》:本文主要介绍SpringBoot整合liteflow的详细过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋...  liteflow 是什么? 能做什么?总之一句话:能帮你规范写代码逻辑 ,编排并解耦业务逻辑,代码

JavaSE正则表达式用法总结大全

《JavaSE正则表达式用法总结大全》正则表达式就是由一些特定的字符组成,代表的是一个规则,:本文主要介绍JavaSE正则表达式用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录常用的正则表达式匹配符正则表China编程达式常用的类Pattern类Matcher类PatternSynta

Spring Security中用户名和密码的验证完整流程

《SpringSecurity中用户名和密码的验证完整流程》本文给大家介绍SpringSecurity中用户名和密码的验证完整流程,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定... 首先创建了一个UsernamePasswordAuthenticationTChina编程oken对象,这是S