Spring Security 實(shí)戰(zhàn)干貨:Spring Boot 中的 Spring Security 自動(dòng)配置初探

1. 前言

我們?cè)谇皫灼獙?duì) Spring Security 的用戶信息管理機(jī)制,密碼機(jī)制進(jìn)行了探討。我們發(fā)現(xiàn) Spring Security Starter相關(guān)的 Servlet 自動(dòng)配置都在spring-boot-autoconfigure-2.1.9.RELEASE(當(dāng)前 Spring Boot 版本為2.1.9.RELEASE) 模塊的路徑org.springframework.boot.autoconfigure.security.servlet 之下。其實(shí)官方提供的Starter組件的自動(dòng)配置你都能在spring-boot-autoconfigure-2.1.9.RELEASE下找到。今天我們進(jìn)一步來(lái)解密 Spring SecuritySpring Boot 的配置和使用。

2. Spring Boot 下 Spring Security 的自動(dòng)配置

我們可以通過(guò) org.springframework.boot.autoconfigure.security.servlet 路徑下找到 Spring Security 關(guān)于Servlet的自動(dòng)配置類(lèi)。我們來(lái)大致了解一下。

2.1 SecurityAutoConfiguration

 package org.springframework.boot.autoconfigure.security.servlet;
 
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.security.SecurityDataConfiguration;
 import org.springframework.boot.autoconfigure.security.SecurityProperties;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.security.authentication.AuthenticationEventPublisher;
 import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
 
 /**
  * {@link EnableAutoConfiguration Auto-configuration} for Spring Security.
  *
  * @author Dave Syer
  * @author Andy Wilkinson
  * @author Madhura Bhave
  * @since 1.0.0
  */
 @Configuration
 @ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
 @EnableConfigurationProperties(SecurityProperties.class)
 @Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
       SecurityDataConfiguration.class })
 public class SecurityAutoConfiguration {
 
   @Bean
   @ConditionalOnMissingBean(AuthenticationEventPublisher.class)
   public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
       return new DefaultAuthenticationEventPublisher(publisher);
   }
 
 }

SecurityAutoConfiguration 顧名思義安全配置類(lèi)。該類(lèi)引入(@import)了 SpringBootWebSecurityConfiguration、WebSecurityEnablerConfigurationSecurityDataConfiguration 三個(gè)配置類(lèi)。 讓這三個(gè)模塊的類(lèi)生效。是一個(gè)復(fù)合配置,是 Spring Security 自動(dòng)配置最重要的一個(gè)類(lèi)之一。 Spring Boot 自動(dòng)配置經(jīng)常使用這種方式以達(dá)到靈活配置的目的,這也是我們研究 Spring Security 自動(dòng)配置的一個(gè)重要入口 同時(shí) SecurityAutoConfiguration 還將 DefaultAuthenticationEventPublisher 作為默認(rèn)的 AuthenticationEventPublisher 注入 Spring IoC 容器。如果你熟悉 Spring 中的事件機(jī)制你就會(huì)知道該類(lèi)是一個(gè) Spring 事件發(fā)布器。該類(lèi)內(nèi)置了一個(gè)HashMap<String, Constructor<? extends AbstractAuthenticationEvent>>維護(hù)了認(rèn)證異常處理和對(duì)應(yīng)異常事件處理邏輯的映射關(guān)系,比如賬戶過(guò)期異常 AccountExpiredException 對(duì)應(yīng)認(rèn)證過(guò)期事件AuthenticationFailureExpiredEvent ,也就是說(shuō)發(fā)生不同認(rèn)證的異常使用不同處理策略。

2.2 SpringBootWebSecurityConfiguration

 @Configuration
 @ConditionalOnClass(WebSecurityConfigurerAdapter.class)
 @ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
 @ConditionalOnWebApplication(type = Type.SERVLET)
 public class SpringBootWebSecurityConfiguration {
 
   @Configuration
   @Order(SecurityProperties.BASIC_AUTH_ORDER)
   static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
 
   }
 
 }

這個(gè)類(lèi)是Spring Security 對(duì) Spring Boot Servlet Web 應(yīng)用的默認(rèn)配置。核心在于WebSecurityConfigurerAdapter 適配器。從 @ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class) 我們就能看出 WebSecurityConfigurerAdapter 是安全配置的核心。 默認(rèn)情況下 DefaultConfigurerAdapter 將以SecurityProperties.BASIC_AUTH_ORDER-5) 的順序注入 Spring IoC 容器,這是個(gè)空實(shí)現(xiàn)。如果我們需要個(gè)性化可以通過(guò)繼承 WebSecurityConfigurerAdapter 來(lái)實(shí)現(xiàn)。我們會(huì)在以后的博文重點(diǎn)介紹該類(lèi)。

2.3 WebSecurityEnablerConfiguration

 @Configuration
 @ConditionalOnBean(WebSecurityConfigurerAdapter.class)
 @ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
 @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
 @EnableWebSecurity
 public class WebSecurityEnablerConfiguration {
 
 }

該配置類(lèi)會(huì)在SpringBootWebSecurityConfiguration 注入 Spring IoC 容器后啟用 @EnableWebSecurity 注解。也就是說(shuō) WebSecurityEnablerConfiguration 目的僅僅就是在某些條件下激活 @EnableWebSecurity 注解。那么這個(gè)注解都有什么呢?

3. @EnableWebSecurity 注解

 @Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
 @Target(value = { java.lang.annotation.ElementType.TYPE })
 @Documented
 @Import({ WebSecurityConfiguration.class,
       SpringWebMvcImportSelector.class,
       OAuth2ImportSelector.class })
 @EnableGlobalAuthentication
 @Configuration
 public @interface EnableWebSecurity {
 
   /**
    * Controls debugging support for Spring Security. Default is false.
    * @return if true, enables debug support with Spring Security
    */
   boolean debug() default false;
 } 

@Enable* 這類(lèi)注解都是帶配置導(dǎo)入的注解。通過(guò)導(dǎo)入一些配置來(lái)啟用一些特定功能。 @EnableWebSecurity 導(dǎo)入了 WebSecurityConfiguration 、SpringWebMvcImportSelector 、OAuth2ImportSelector 以及啟用了 @EnableGlobalAuthentication注解。

3.1 WebSecurityConfiguration

該配置類(lèi)WebSecurityConfiguration使用一個(gè)WebSecurity對(duì)象基于用戶指定的或者默認(rèn)的安全配置,你可以通過(guò)繼承 WebSecurityConfigurerAdapter 或者實(shí)現(xiàn) WebSecurityConfigurer 來(lái)定制 WebSecurity 創(chuàng)建一個(gè)FilterChainProxy Bean來(lái)對(duì)用戶請(qǐng)求進(jìn)行安全過(guò)濾。這個(gè)FilterChainProxy的名稱(chēng)就是 WebSecurityEnablerConfiguration上的 BeanIds.SPRING_SECURITY_FILTER_CHAIN 也就是 springSecurityFilterChain,它是一個(gè)Filter,最終會(huì)被作為Servlet過(guò)濾器鏈中的一個(gè)Filter應(yīng)用到Servlet容器中。安全處理的策略主要是過(guò)濾器的調(diào)用順序。WebSecurityConfiguration 最終會(huì)通過(guò) @EnableWebSecurity 應(yīng)用到系統(tǒng)。

源碼分析:

 package org.springframework.security.config.annotation.web.configuration;
 
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
 import javax.servlet.Filter;
 
 import org.springframework.beans.factory.BeanClassLoaderAware;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.DependsOn;
 import org.springframework.context.annotation.ImportAware;
 import org.springframework.core.OrderComparator;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.AnnotationAttributes;
 import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.core.annotation.Order;
 import org.springframework.core.type.AnnotationMetadata;
 import org.springframework.security.access.expression.SecurityExpressionHandler;
 import org.springframework.security.config.annotation.ObjectPostProcessor;
 import org.springframework.security.config.annotation.SecurityConfigurer;
 import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
 import org.springframework.security.config.annotation.web.builders.WebSecurity;
 import org.springframework.security.context.DelegatingApplicationListener;
 import org.springframework.security.web.FilterChainProxy;
 import org.springframework.security.web.FilterInvocation;
 import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
 import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
 
 
 /**
  * Spring Web Security 的配置類(lèi) : 
  *  1. 使用一個(gè) WebSecurity 對(duì)象基于安全配置創(chuàng)建一個(gè) FilterChainProxy 對(duì)象來(lái)對(duì)用戶請(qǐng)求進(jìn)行安全過(guò)濾。 
  *  2. 也會(huì)暴露諸如 安全SpEL表達(dá)式處理器 SecurityExpressionHandler 等一些類(lèi)。
  *   
  * @see EnableWebSecurity
  * @see WebSecurity
  *
  * @author Rob Winch
  * @author Keesun Baik
  * @since 3.2
  */
 @Configuration
 public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
   private WebSecurity webSecurity;
  // 是否啟用了調(diào)試模式,來(lái)自注解 @EnableWebSecurity 的屬性 debug,缺省值 false
   private Boolean debugEnabled;
 
   private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
 
   private ClassLoader beanClassLoader;
 
   @Autowired(required = false)
   private ObjectPostProcessor<Object> objectObjectPostProcessor;
   /**
    *
    * 代理監(jiān)聽(tīng)器 應(yīng)該時(shí)監(jiān)聽(tīng) DefaultAuthenticationEventPublisher 的一些處理策略
    */   
   @Bean
   public static DelegatingApplicationListener delegatingApplicationListener() {
       return new DelegatingApplicationListener();
   }
    /**
     *
     * 安全SpEL表達(dá)式處理器 SecurityExpressionHandler 缺省為一個(gè) DefaultWebSecurityExpressionHandler
     */   
   @Bean
   @DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
   public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
       return webSecurity.getExpressionHandler();
   }
 
   /**
    *  Spring Security 核心過(guò)濾器  Spring Security Filter Chain  , Bean ID 為 springSecurityFilterChain
    * @return the {@link Filter} that represents the security filter chain
    * @throws Exception
    */
   @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
   public Filter springSecurityFilterChain() throws Exception {
       boolean hasConfigurers = webSecurityConfigurers != null
               && !webSecurityConfigurers.isEmpty();
       if (!hasConfigurers) {
           WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
                   .postProcess(new WebSecurityConfigurerAdapter() {
                   });
           webSecurity.apply(adapter);
       }
       return webSecurity.build();
   }
 
   /**
    *
    * 用于模板 如JSP Freemarker 的一些頁(yè)面標(biāo)簽按鈕控制支持
    * Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary for the JSP
    * tag support.
    * @return the {@link WebInvocationPrivilegeEvaluator}
    * @throws Exception
    */
   @Bean
   @DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
   public WebInvocationPrivilegeEvaluator privilegeEvaluator() throws Exception {
       return webSecurity.getPrivilegeEvaluator();
   }
 
   /**
    *
    * 用于創(chuàng)建web configuration的SecurityConfigurer實(shí)例,
    * 注意該參數(shù)通過(guò)@Value(...)方式注入,對(duì)應(yīng)的bean autowiredWebSecurityConfigurersIgnoreParents
    * 也在該類(lèi)中定義
    *
    * @param objectPostProcessor the {@link ObjectPostProcessor} used to create a
    * {@link WebSecurity} instance
    * @param webSecurityConfigurers the
    * {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to
    * create the web configuration
    * @throws Exception
    */
   @Autowired(required = false)
   public void setFilterChainProxySecurityConfigurer(
           ObjectPostProcessor<Object> objectPostProcessor,
           @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
           throws Exception {
       webSecurity = objectPostProcessor
               .postProcess(new WebSecurity(objectPostProcessor));
       if (debugEnabled != null) {
           webSecurity.debug(debugEnabled);
       }
 
       Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
 
       Integer previousOrder = null;
       Object previousConfig = null;
       for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
           Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
           if (previousOrder != null && previousOrder.equals(order)) {
               throw new IllegalStateException(
                       "@Order on WebSecurityConfigurers must be unique. Order of "
                               + order + " was already used on " + previousConfig + ", so it cannot be used on "
                               + config + " too.");
           }
           previousOrder = order;
           previousConfig = config;
       }
       for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
           webSecurity.apply(webSecurityConfigurer);
       }
       this.webSecurityConfigurers = webSecurityConfigurers;
   }
    /**
     * 從當(dāng)前bean容器中獲取所有的WebSecurityConfigurer bean。
     * 這些WebSecurityConfigurer通常是由開(kāi)發(fā)人員實(shí)現(xiàn)的配置類(lèi),并且繼承自WebSecurityConfigurerAdapter
     *  
     */   
   @Bean
   public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
           ConfigurableListableBeanFactory beanFactory) {
       return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
   }
 
   /**
    * A custom verision of the Spring provided AnnotationAwareOrderComparator that uses
    * {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class
    * instances for the {@link Order} annotation.
    *
    * @author Rob Winch
    * @since 3.2
    */
   private static class AnnotationAwareOrderComparator extends OrderComparator {
       private static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();
 
       @Override
       protected int getOrder(Object obj) {
           return lookupOrder(obj);
       }
 
       private static int lookupOrder(Object obj) {
           if (obj instanceof Ordered) {
               return ((Ordered) obj).getOrder();
           }
           if (obj != null) {
               Class<?> clazz = (obj instanceof Class ? (Class<?>) obj : obj.getClass());
               Order order = AnnotationUtils.findAnnotation(clazz, Order.class);
               if (order != null) {
                   return order.value();
               }
           }
           return Ordered.LOWEST_PRECEDENCE;
       }
   }
 
   /*
    * 要是為了獲取注解 @EnableWebSecurity 的屬性 debugEnabled
    *
    * @see org.springframework.context.annotation.ImportAware#setImportMetadata(org.
    * springframework.core.type.AnnotationMetadata)
    */
   public void setImportMetadata(AnnotationMetadata importMetadata) {
       Map<String, Object> enableWebSecurityAttrMap = importMetadata
               .getAnnotationAttributes(EnableWebSecurity.class.getName());
       AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes
               .fromMap(enableWebSecurityAttrMap);
       debugEnabled = enableWebSecurityAttrs.getBoolean("debug");
       if (webSecurity != null) {
           webSecurity.debug(debugEnabled);
       }
   }
 
   /*
    * (non-Javadoc)
    *
    * @see
    * org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.
    * lang.ClassLoader)
    */
   public void setBeanClassLoader(ClassLoader classLoader) {
       this.beanClassLoader = classLoader;
   }
 }

3.2 SpringWebMvcImportSelector

該類(lèi)是為了對(duì) Spring Mvc 進(jìn)行支持的。一旦發(fā)現(xiàn)應(yīng)用使用 Spring Mvc 的核心前置控制器 DispatcherServlet 就會(huì)引入 WebMvcSecurityConfiguration 。主要是為了適配 Spring Mvc 。

3.3 OAuth2ImportSelector

該類(lèi)是為了對(duì) OAuth2.0 開(kāi)放授權(quán)協(xié)議進(jìn)行支持。ClientRegistration 如果被引用,具體點(diǎn)也就是 spring-security-oauth2 模塊被啟用(引入依賴jar)時(shí)。會(huì)啟用 OAuth2 客戶端配置 OAuth2ClientConfiguration 。

3.4 @EnableGlobalAuthentication

這個(gè)類(lèi)主要引入了 AuthenticationConfiguration 目的主要為了構(gòu)造 認(rèn)證管理器 AuthenticationManagerAuthenticationManager 十分重要后面我們會(huì)進(jìn)行專(zhuān)門(mén)的分析。

4. SecurityFilterAutoConfiguration

我們?cè)?org.springframework.boot.autoconfigure.security.servlet 路徑下還發(fā)現(xiàn)了一個(gè)配置類(lèi) SecurityFilterAutoConfiguration 。該類(lèi)用于向Servlet容器注冊(cè)一個(gè)名稱(chēng)為securityFilterChainRegistration的bean, 實(shí)現(xiàn)類(lèi)是DelegatingFilterProxyRegistrationBean。該 bean 的目的是注冊(cè)另外一個(gè) Servlet Filter BeanServlet 容器,實(shí)現(xiàn)類(lèi)為 DelegatingFilterProxy 。DelegatingFilterProxy 其實(shí)是一個(gè)代理過(guò)濾器,它被 Servlet 容器用于處理請(qǐng)求時(shí),會(huì)將任務(wù)委托給指定給自己另外一個(gè)Filter bean。對(duì)于 SecurityFilterAutoConfiguration,來(lái)講,這個(gè)被代理的Filter bean的名字為 springSecurityFilterChain , 也就是我們上面提到過(guò)的 Spring Security Web提供的用于請(qǐng)求安全處理的Filter bean,其實(shí)現(xiàn)類(lèi)是 FilterChainProxy。

相關(guān)的源碼分析:

 package org.springframework.boot.autoconfigure.security.servlet;
 
 import java.util.EnumSet;
 import java.util.stream.Collectors;
 
 import javax.servlet.DispatcherType;
 
 import org.springframework.boot.autoconfigure.AutoConfigureAfter;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
 import org.springframework.boot.autoconfigure.security.SecurityProperties;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
 import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
 
 @Configuration
 // 僅在 Servlet 環(huán)境下生效
 @ConditionalOnWebApplication(type = Type.SERVLET)
 // 確保安全屬性配置信息被加載并以bean形式被注冊(cè)到容器
 @EnableConfigurationProperties(SecurityProperties.class)
 // 僅在特定類(lèi)存在于 classpath 上時(shí)才生效
 @ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class,
       SessionCreationPolicy.class })
 // 指定該配置類(lèi)在  SecurityAutoConfiguration 配置類(lèi)應(yīng)用之后應(yīng)用       
 @AutoConfigureAfter(SecurityAutoConfiguration.class)
 public class SecurityFilterAutoConfiguration {
 
     // 要注冊(cè)到 Servlet 容器的 DelegatingFilterProxy Filter的 
     // 目標(biāo)代理Filter bean的名稱(chēng) :springSecurityFilterChain
   private static final String DEFAULT_FILTER_NAME = 
           AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
 
 
     // 定義一個(gè) bean securityFilterChainRegistration, 
     // 該 bean 的目的是注冊(cè)另外一個(gè) bean 到 Servlet 容器 : 實(shí)現(xiàn)類(lèi)為 DelegatingFilterProxy 的一個(gè) Servlet Filter
     // 該 DelegatingFilterProxy Filter 其實(shí)是一個(gè)代理過(guò)濾器,它被 Servlet 容器用于匹配特定URL模式的請(qǐng)求,
     // 而它會(huì)將任務(wù)委托給指定給自己的名字為 springSecurityFilterChain 的 Filter, 也就是 Spring Security Web
     // 提供的用于請(qǐng)求安全處理的一個(gè) Filter bean,其實(shí)現(xiàn)類(lèi)是 FilterChainProxy
     // (可以將 1 個(gè) FilterChainProxy 理解為 1 HttpFirewall + n SecurityFilterChain)
   @Bean
   @ConditionalOnBean(name = DEFAULT_FILTER_NAME)
   public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
           SecurityProperties securityProperties) {
       DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
               DEFAULT_FILTER_NAME);
       registration.setOrder(securityProperties.getFilter().getOrder());
       registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
       return registration;
   }
 
   private EnumSet<DispatcherType> getDispatcherTypes(
           SecurityProperties securityProperties) {
       if (securityProperties.getFilter().getDispatcherTypes() == null) {
           return null;
       }
       return securityProperties.getFilter().getDispatcherTypes().stream()
               .map((type) -> DispatcherType.valueOf(type.name())).collect(Collectors
                       .collectingAndThen(Collectors.toSet(), EnumSet::copyOf));
   }
 
 }
 

5. 總結(jié)

本文主要對(duì) Spring Security 在 Spring Boot 中的自動(dòng)配置一些機(jī)制進(jìn)行了粗略的講解。為什么沒(méi)有細(xì)講。因?yàn)閺膶W(xué)習(xí)出發(fā)有些東西不是我們必須要深入了解的,但是又要知道一點(diǎn)點(diǎn)相關(guān)的知識(shí)。我們先宏觀上有個(gè)大致的了解就行。所以在閱讀本文一定不要鉆牛角尖。粗略知道配置策略、加載策略和一些關(guān)鍵類(lèi)的作用即可。在你對(duì) Spring Security 有了進(jìn)一步學(xué)習(xí)之后,回頭認(rèn)真來(lái)看這些配置類(lèi)會(huì)有更深層的思考。 從另一個(gè)方面該文也給你閱讀 Spring 源碼提供了一些思路,學(xué)會(huì)這些才是最重要的。

關(guān)注公眾號(hào):碼農(nóng)小胖哥,獲取更多資訊

個(gè)人博客:https://felord.cn

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容