SpringSecurity源码分析(一) SpringBoot集成SpringSecurity即Spring安全框架的加载过程

news2024/9/22 7:38:02

      Spring Security是一个强大的并且高度可定制化的访问控制框架。 它基于spring应用。

Spring Security是聚焦于为java应用提供授权和验证的框架。像所有的spring项目一样,Spring Security真正的强大在于可以非常简单的拓展功能来实现自定义的需求。

      在分析SpringBoot集成的SpringSecurity源码时,一般可以分为两部分来分析Spring安全框架的源码。

      一、SpringSecurity在SpringBoot框架的启动过程中的加载过程。

      二、SpringSecurity在请求执行过程当中的执行过程。

     现在我根据上面的两个过程对SpringSecurity的源码进行分析。

     在分析时我们需要在springboot项目中引入SpringSecurity的maven依赖配置。该配置如下所示:

        <!--spring 安全框架-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

注意:我分析的springsecurity版本是5.0.7-RELEASE 

 SpringSecurity在SpringBoot框架的启动过程中的加载过程。

     SpringSecurity框架在调用过程中通过配置的方式往spring容器中注入很多bean,为调用过程中做准备。

     在SpringSecurity中存在很多配置类,负责在springboot启动时往容器注入bean。本文主要分析一下设计注入的类。

     在SpringSecurity框架中有三个非常核心的类和接口,分别是

            1.SecurityFilterChain接口

            2.FilterChainProxy类

            3.DelegatingFilterProxy类

这个三个接口和类的相互之间的步骤关系如下:

    第一步:生成一个FilterChainProxy类型的对象,其中的属性filterChains是SecurityFilterChain类型的List集合,该对象是一个被spring容器管理名称为springSecurityFilterChain类型为FilterChainProxy的bean。

    第二步:生成一个DelegatingFilterProxy类型的对象,将beanName即springSecurityFilterChain作为DelegatingFilterProxy类型对象属性targetBeanName的值,供后面请求时获取bean。这样FilterChainProxy类型的对象就被DelegatingFilterProxy类型的对象委托管理了。

      DelegatingFilterProxy对象的生成是tomcat启动过程中会调用所有继承了RegistrationBean类的

onStartUp方法,最终调用了实现类中的addRegistration方法。RegistrationBean类中onStartUp方法的调用逻辑可以参考我写的springMvc分析第一章

SpringMvc源码分析(一):启动tomcat服务器,加载DispatcherServlet并将DispatcherServlet纳入tomcat管理_xl649138628的博客-CSDN博客

其调用链路是

    +RegistrationBean#onStartup

        +DynamicRegistrationBean#register

           +AbstractFilterRegistrationBean#addRegistration(往StandardContext设置拦截器类型为DelegatingFilterProxy)

              +DelegatingFilterProxyRegistrationBean#getFilter()

第三步:前端发起请求时,调用了DelegatingFilterProxy类型的拦截器执行doFilter方法。doFilter方法获取被委托的对象FilterChainProxy并调用其doFilter方法。FilterChainProxy的doFilter方法,执行获取到的所有的拦截器然后再获取代理对象执行容器加载时保存的拦截器再执行。

本文主要分析第一步和第二步。

1.生成一个FilterChainProxy类型的对象

   1.1分析配置文件并生成beanName为springSecurityFilterChain的Bean

    1.1.1 SpringSecurity拦截器示意图

    在SpringSecurity官方文档中可以看到如下示意图。

    该图中清楚的可以看到请求访问时,DelegatingFilterProxy管理FilterChainProxy,FilterChainProxy里调用SecurityFilterChain类型的过滤器。

在这里插入图片描述

下面我会详细的分析这三个类和对象是怎么关联起来 。

 1.1.2 安全框架的配置类及如何加载

    在springboot启动过程中会获取spring.factories配置文件里的配置类并加载到spring容器中,观察spring.factories配置文件里的配置内容,涉及到springsecurity的如下图红框处所示。

      我们首先关注SecurityAutoConfiguration和SecurityFilterAutoConfiguration这两个配置类。

深入分析这些类,我们可以看到配置类的关系如下图所示:

1.1.3配置类是如何获取到 springSecurityFilterChain这个bean的

 首先分析SecurityAutoConfiguration配置类

@Configuration
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
//导入属性配置文件,内部声明了user类
@EnableConfigurationProperties(SecurityProperties.class)
//导入并加载下面三个配置类到Spring容器管理
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
		SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
    //发布认证事件,当AuthenticationEventPublisher bean不存在时加载
	@Bean
	@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
	public DefaultAuthenticationEventPublisher authenticationEventPublisher(
			ApplicationEventPublisher publisher) {
        //内部实现就是spring的ApplicationEventPublisher,
        // 用于springsecurity各种权限时间的交互,如登陆失败,会发布一个事件,
        // 然后通知其它组件做出相应的响应
		return new DefaultAuthenticationEventPublisher(publisher);
	}

}

分析该配置类里导入的三个配置类SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,SecurityDataConfiguration.class

1.SpringBootWebSecurityConfiguration配置类

作用是WebSecurityConfigurerAdapter 类存在但是bean对象不存在时注册默认的WebSecurityConfigurerAdapter 类型是DefaultConfigurerAdapter的bean。

@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {
    //往容器中注入WebSecurityConfigurerAdapter类型的Bean,后面的逻辑会使用
	@Configuration
	@Order(SecurityProperties.BASIC_AUTH_ORDER)
	static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {

	}

}

2.WebSecurityEnablerConfiguration配置类

该类文件如下,可以看到其中声明了@EnableWebSecurity注解

//WebSecurityConfigurerAdapter bean对象存在
@ConditionalOnBean(WebSecurityConfigurerAdapter.class)
//没有名称为springSecurityFilterChain的bean
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {

}

进入@EnableWebSecurity注解

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
		SpringWebMvcImportSelector.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;
}

可以看到引入了WebSecurityConfiguration和SpringWebMvcImportSelector两个配置类,声明了@EnableGlobalAuthentication注解。先研究WebSecurityConfiguration类

2.1WebSecurityConfiguration类

    类中的英文注释如下:

 * Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web
 * based security for Spring Security. It then exports the necessary beans. Customizations
 * can be made to {@link WebSecurity} by extending {@link WebSecurityConfigurerAdapter}
 * and exposing it as a {@link Configuration} or implementing
 * {@link WebSecurityConfigurer} and exposing it as a {@link Configuration}. This
 * configuration is imported when using {@link EnableWebSecurity}.

意思是:基于SpringSecurity框架的安全性执行web使用WebSecurity去创建一个FilterChainProxy。然后输出需要的bean.通过继承WebSecurityConfigurerAdapter并且声明为一个Configuration配置或者继承WebSecurityConfigurer并且声明为一个Configuration配置来实现定制化。这个WebSecurityConfiguration类是通过EnableWebSecurity注解引入的。

分析WebSecurityConfiguration类重点关注以下代码

	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) { //注意hasConfigurers为true,此部分逻辑不执行
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
            //往AbstractConfiguredSecurityBuilder类型对象的configurers属性
            //(该属性是LinkedHashMap类型)中添加了一个WebSecurityConfigurerAdapter类型的数据
			webSecurity.apply(adapter);
		}
		return webSecurity.build();
	}

该端代码往spring容器中注入了一个名称为springSecurityFilterChain的bean。

1.1.4分析springSecurityFilterChain,并分析springSecurityFilterChain是如何获取到过滤器并管理过滤器的

再源码中可以看到这段代码

WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
      .postProcess(new WebSecurityConfigurerAdapter() {
      });

该代码是通过 objectObjectPostProcessor调用的,objectObjectPostProcessor对象是通过

	@Autowired(required = false)
	private ObjectPostProcessor<Object> objectObjectPostProcessor;

注入到WebSecurityConfiguration配置类对象里的。objectObjectPostProcessor这个bean是通过@EnableWebSecurity注解里的@EnableGlobalAuthentication里的引入的AuthenticationConfiguration.class里引入的ObjectPostProcessorConfiguration.class注入的。注入代码如下:

@Configuration
public class ObjectPostProcessorConfiguration {

	@Bean
	public ObjectPostProcessor<Object> objectPostProcessor(
			AutowireCapableBeanFactory beanFactory) {
		return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
	}
}

继续回到objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { });方法。因为objectObjectPostProcessor是AutowireBeanFactoryObjectPostProcessor类型的,所以调用的是AutowireBeanFactoryObjectPostProcessor的postProcess方法。

在postProcess方法的源码中,可以看到其将传入的对象生成一个bean并注入到spring容器中。

由于这个对象是new WebSecurityConfigurerAdapter()生成的,所以生成的是一个WebSecurityConfigurerAdapter类型的bean。

public <T> T postProcess(T object) {
		if (object == null) {
			return null;
		}
		T result = null;
		try {
			result = (T) this.autowireBeanFactory.initializeBean(object,
					object.toString());
		}
		catch (RuntimeException e) {
			Class<?> type = object.getClass();
			throw new RuntimeException(
					"Could not postProcess " + object + " of type " + type, e);
		}
		this.autowireBeanFactory.autowireBean(object);
		if (result instanceof DisposableBean) {
			this.disposableBeans.add((DisposableBean) result);
		}
		if (result instanceof SmartInitializingSingleton) {
			this.smartSingletons.add((SmartInitializingSingleton) result);
		}
		return result;
	}

继续分析springSecurityFilterChain方法里的webSecurity.apply(adapter)方法。我们发现webSecurity对象的赋值是在以下源码中实现的

	@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);
		}
        //根据继承WebSecurityConfigurer配置类的@Order注解进行排序
		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);
		}
        //将自定义继承了WebSecurityConfigurer的配置类集合赋值给webSecurityConfigurers属性
		this.webSecurityConfigurers = webSecurityConfigurers;
	}

在上面的代码中我们发现入参

@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers

这段代码的意思是调用autowiredWebSecurityConfigurersIgnoreParents对象里的getWebSecurityConfigurers方法。autowiredWebSecurityConfigurersIgnoreParents对象是通过WebSecurityConfiguration类里的以下方法注入的。

	@Bean
	public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
			ConfigurableListableBeanFactory beanFactory) {
		return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
	}

观察AutowiredWebSecurityConfigurersIgnoreParents类里的getWebSecurityConfigurers方法。

该方法的作用是获取WebSecurityConfigurer类型的bean。并返回一个list集合。该bean对象是开发者自定义的各种各样继承自WebSecurityConfigurerAdapter的配置类。如果开发者没有自定义任何配置类,那么这里获取到的就是前面所讲的SpringBootWebSecurityConfiguration 类中提供的默认配置类,将获取到的所有配置类实例放入webSecurityConfigurers集合中并返回。

	public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
		List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
		Map<String, WebSecurityConfigurer> beansOfType = beanFactory
				.getBeansOfType(WebSecurityConfigurer.class);
        //循环将所有的过滤器放到webSecurityConfigurers属性中。
		for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
			webSecurityConfigurers.add(entry.getValue());
		}
		return webSecurityConfigurers;
	}

另外一个入参ObjectPostProcessor<Object> objectPostProcessor 该对象是Spring注入的AutowireBeanFactoryObjectPostProcessor的bean.该bean的生成在上文有分析过。

webSecurity = objectPostProcessor
      .postProcess(new WebSecurity(objectPostProcessor));

该段代码生成了一个WebSecurity 类型的bean并对WebSecurityConfiguration类的webSecurity属性赋值。

		for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
			webSecurity.apply(webSecurityConfigurer);
		}

在上面的代码中循环获取到的自定义配置类。看见如下代码

	public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
		add(configurer);
		return configurer;
	}

重点关注上面add方法,源码如下:

	private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
		Assert.notNull(configurer, "configurer cannot be null");

		Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
				.getClass();
		synchronized (configurers) {
			if (buildState.isConfigured()) {
				throw new IllegalStateException("Cannot apply " + configurer
						+ " to already built object");
			}
			List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
					.get(clazz) : null;
			if (configs == null) {
				configs = new ArrayList<SecurityConfigurer<O, B>>(1);
			}
			configs.add(configurer);
            //给configurers属性里放入以类名为Key,List<SecurityConfigurer<O, B>>为value
            //放入List<SecurityConfigurer<O, B>>里的是开发的自定义拦截器
			this.configurers.put(clazz, configs);
			if (buildState.isInitializing()) {
				this.configurersAddedInInitializing.add(configurer);
			}
		}
	}

这样所有的开发自定义的继承自WebSecurityConfigurer的配置类和SpringBootWebSecurityConfiguration配置类中通过@Bean方式纳入Spring容器管理的继承自WebSecurityConfigurer的WebSecurityConfigurerAdapter类都放到了AbstractConfiguredSecurityBuilder类的configurers属性中供后面调用。

即configurers属性值 = 开发自定的继承自WebSecurityConfigurer配置类+SpringBootWebSecurityConfiguration配置类导入的WebSecurityConfigurerAdapter类。

1.2 分析FilterChainProxy类型的代理对象是如何生成的,并且是如何管理springSecurityFilterChain 这个bean的。

继续分析springSecurityFilterChain方法里的webSecurity.build()方法。在前面我们知道了webSecurity对象是WebSecurity类型的。

	public final O build() throws Exception {
		if (this.building.compareAndSet(false, true)) {
            //关注此方法
			this.object = doBuild();
			return this.object;
		}
		throw new AlreadyBuiltException("This object has already been built");
	}
	@Override
	protected final O doBuild() throws Exception {
		synchronized (configurers) {
			buildState = BuildState.INITIALIZING;
            //待实现方法
			beforeInit();
            //调用的是AbstractConfiguredSecurityBuilder类里的init方法
			init();

			buildState = BuildState.CONFIGURING;
            //待实现方法
			beforeConfigure();
            //调用的是AbstractConfiguredSecurityBuilder类里的configure方法
			configure();

			buildState = BuildState.BUILDING;
            //调用的是WebSecurity类里的performBuild方法。
			O result = performBuild();

			buildState = BuildState.BUILT;

			return result;
		}
	}

 其中init方法调用了开发者自定义的实现了WebSecurityConfigurer

的配置类里的init方法及WebSecurityConfigurerAdapter类里的init方法。

以下是

	private void init() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.init((B) this);
		}

		for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
			configurer.init((B) this);
		}
	}
	public void init(final WebSecurity web) throws Exception {
        //生成一个HttpSecurity类型的对象
		final HttpSecurity http = getHttp();
        //addSecurityFilterChainBuilder对securityFilterChainBuilders属性赋值
		web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
			public void run() {
				FilterSecurityInterceptor securityInterceptor = http
						.getSharedObject(FilterSecurityInterceptor.class);
				web.securityInterceptor(securityInterceptor);
			}
		});
	}

 getHttp方法用于生成一个HttpSecurity类型的对象。

	protected final HttpSecurity getHttp() throws Exception {
		if (http != null) {
			return http;
		}

		DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
				.postProcess(new DefaultAuthenticationEventPublisher());
		localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);

		AuthenticationManager authenticationManager = authenticationManager();
		authenticationBuilder.parentAuthenticationManager(authenticationManager);
		Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();

		http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
				sharedObjects);
		if (!disableDefaults) {
			// @formatter:off
			http
				.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();
            //SpringFactoriesLoader类的主要作用是通过类路径下的META-INF/spring.factories文件 
            //获取工厂类接口的实现类,初始化并保存在缓存中,以供Springboot启动过程中各个阶段的调用。
            //SpringFactoriesLoader.loadFactories():是根据参数factoryClass获取spring.factories下配置的所有实现类实例,返回List<T>的。
            //SpringFactoriesLoader.loadFactoryNames():是根据参数factoryClass获取spring.factories下配置
			List<AbstractHttpConfigurer> defaultHttpConfigurers =
					SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
            //将所有AbstractHttpConfigurer类型的对象放入到AbstractConfiguredSecurityBuilder类的configurers属性中供后面调用
			for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
				http.apply(configurer);
			}
		}
        //配置HttpSecurity类型对象的属性
		configure(http);
		return http;
	}

 其中configure方法调用了开发者自定义的继承自WebSecurityConfigurerAdapter的配置类里的configure方法

	private void configure() throws Exception {
		Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

		for (SecurityConfigurer<O, B> configurer : configurers) {
			configurer.configure((B) this);
		}
	}

 其中performBuild调用的是WebSecurity类里的performBuild方法,该方法的作用是FilterChainProxy类型的代理对象,该代理对象将拦截器链纳入代理对象管理。

	@Override
	protected Filter performBuild() throws Exception {
		Assert.state(
				!securityFilterChainBuilders.isEmpty(),
				"At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke "
						+ WebSecurity.class.getSimpleName()
						+ ".addSecurityFilterChainBuilder directly");
		int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
		List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
				chainSize);
		for (RequestMatcher ignoredRequest : ignoredRequests) {
			securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
		}
        //securityFilterChainBuilders取到的是HttpSecurity类型的对象
		for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
			securityFilterChains.add(securityFilterChainBuilder.build());
		}
        //重点关注此处,将拦截器链纳入filterChainProxy代理类管理,如果不扩展securityFilterChainBuilders属性,里面只有一个对象时HttpSecurity类型的。
		FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
		if (httpFirewall != null) {
			filterChainProxy.setFirewall(httpFirewall);
		}
		filterChainProxy.afterPropertiesSet();

		Filter result = filterChainProxy;
		if (debugEnabled) {
			logger.warn("\n\n"
					+ "********************************************************************\n"
					+ "**********        Security debugging is enabled.       *************\n"
					+ "**********    This may include sensitive information.  *************\n"
					+ "**********      Do not use in a production system!     *************\n"
					+ "********************************************************************\n\n");
			result = new DebugFilter(filterChainProxy);
		}
        //执行WebSecurityConfigurerAdapter类init方法里传入的 run方法,该方法用于
        //给filterSecurityInterceptor属性赋值   
		postBuildAction.run();
        返回的是FilterChainProxy 类型的对象
		return result;
	}

这样 springSecurityFilterChain这个Bean名实际上对应的是FilterChainProxy 类型的对象。

2. 生成一个DelegatingFilterProxy类型的对象.

分析spring.factories配置文件里的配置类SecurityFilterAutoConfiguration。

可以看到如下源码:

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
//加载SecurityProperties配置类
@EnableConfigurationProperties(SecurityProperties.class)
//AbstractSecurityWebApplicationInitializer和SessionCreationPolicy存在再执行配置
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class,
		SessionCreationPolicy.class })
//在SecurityAutoConfiguration加载完后再执行配置
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
    //省略。。。。。
}

在该配置类中通过@Bean的方式往spring容器中注入了一个bean,该bean的类型是 DelegatingFilterProxyRegistrationBean。

	@Bean
	@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
	public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
			SecurityProperties securityProperties) {
        //初始化对象,并对属性targetBeanName赋值为springSecurityFilterChain
		DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
				DEFAULT_FILTER_NAME);
		registration.setOrder(securityProperties.getFilter().getOrder());
		registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
		return registration;
	}

查看DelegatingFilterProxyRegistrationBean的继承实现树,发现该类实现了RegistrationBean。RegistrationBean中有一个onStartup方法,在SpringBoot启动的过程中,tomcat容器会调用该方法。

在onStartUp方法中存在register,根据动态绑定机制执行的是DynamicRegistrationBean里的register方法。该register方法里调用了addRegistration方法。该addRegistration方法是一个抽象方法。调用的是AbstractFilterRegistrationBean里的addRegistration方法。

	protected Dynamic addRegistration(String description, ServletContext servletContext) {
		Filter filter = getFilter();
		return servletContext.addFilter(getOrDeduceName(filter), filter);
	}

 在AbstractFilterRegistrationBean中getFilter是调用的DelegatingFilterProxyRegistrationBean里的getFilter方法。在该方法中this.targetBeanName属性值是springSecurityFilterChain字符串。

	@Override
	public DelegatingFilterProxy getFilter() {
		return new DelegatingFilterProxy(this.targetBeanName,
				getWebApplicationContext()) {

			@Override
			protected void initFilterBean() throws ServletException {
				// Don't initialize filter bean on init()
			}

		};
	}

 分析getFilter方法该方法最终返回了一个DelegatingFilterProxy类型的对象。继续分析AbstractFilterRegistrationBean里的addRegistration方法。在addRegistration里执行完getFilter方法后,程序继续执行了servletContext.addFilter(getOrDeduceName(filter), filter)这段代码。在这段代码中

	@Override
	protected Dynamic addRegistration(String description, ServletContext servletContext) {
		Filter filter = getFilter();
		return servletContext.addFilter(getOrDeduceName(filter), filter);
	}

   一、servletContext是Tomcat里的ApplicationContextFacade类型的对象。

    public ServletContext getServletContext() {

        if (context == null) {
            context = new ApplicationContext(this);
            if (altDDName != null)
                context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
        }
        return (context.getFacade());

    }

二、ApplicationContextFacade通过addFilter方法往Tomcat的Servlet里添加了拦截器。这样拦截器就被Tomcat管理了。

    @Override
    public FilterRegistration.Dynamic addFilter(String filterName,
            Filter filter) {
        if (SecurityUtil.isPackageProtectionEnabled()) {
            return (FilterRegistration.Dynamic) doPrivileged("addFilter",
                    new Class[]{String.class, Filter.class},
                    new Object[]{filterName, filter});
        } else {
            //设置的filterName为springSecurityFilterChain,即FilterChainProxy类型
            return context.addFilter(filterName, filter);
        }
    }

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

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

相关文章

张晨光-JAVA零基础保姆式技术教程之-事务

事务 课程目标 1、什么是事务 2、jdbc如何控制事务 3、设置事务的回滚点 4、事务的特性ACID 5、数据库事务的隔高级别事务理解 什么是事务&#xff1a; 指逻辑上一组操作&#xff0c;要么同时成功&#xff0c;要么同时失败。 举例&#xff1a; 转账 a 给b 转账 100 a原来有…

React Native Cannot run program “node“问题

概述 前几天mac重装系统了&#xff0c;用Android studio重新构建React native项目时&#xff0c;报Cannot run program "node"错误。 电脑系统为macOS 12.6.3 (Monterey)&#xff0c;M1 Pro芯片。设备信息如下图所示&#xff1a; 完整错误信息如下图所示&#xff…

链表OJ(七)删除有序链表中重复的元素-I -II

目录 删除有序链表中重复的元素-I 删除有序链表中重复的元素-II 删除有序链表中重复的元素-I 描述 删除给出链表中的重复元素&#xff08;链表中元素从小到大有序&#xff09;&#xff0c;使链表中的所有元素都只出现一次 例如&#xff1a; 给出的链表为1→1→21→1→2,返回1…

码农饭碗不保——ChatGPT正在取代Coder

码农饭碗不保——ChatGPT正在取代Coder 最近被OpenAI的ChatGPT刷屏了。我猜你已经读了很多关于ChatGPT的文章&#xff0c;不需要再介绍了。假如碰巧您还不太了解ChatGPT是什么&#xff0c;可以先看一下这篇文章&#xff0c;然后再回来继续。 与ChatGPT对话很有趣&#xff0c;…

【尊享版】如何从零到一掌控习惯?

超友们&#xff0c;早上好&#xff5e; 今天为你带来的分享是《如何从零到一掌控习惯&#xff1f;》&#xff0c;主要分为八个部分&#xff1a; 一、【为什么要培养习惯&#xff1f;】 二、【养成新习惯为什么很难&#xff1f;】 三、【习惯养成的原理是什么&#xff1f;】 …

D3.js绘图流程

简介 D3.js是由javaScript语言编写绘图库&#xff0c;其原理是&#xff1a;通过调用一系列内置函数&#xff0c;生成SVG&#xff0c;并在网页渲染。 本文以频率分布直方图案例为例&#xff0c;介绍使用D3.js的流程 流程 第一步 引用D3.js库 建立一个html文件&#xff0c;在…

【Flutter】入门Dart语言:初步了解内建类型

文章目录一、概述1.为什么叫内建类型&#xff1f;2.Dart的内建类型有哪些&#xff1f;二、内建类型详解1.num2.int3.double4.String5.bool6.List7.Map8.Function三、总结一、概述 "The greatest glory in living lies not in never falling, but in rising every time we…

分享111个HTML体育竞技模板,总有一款适合您

分享111个HTML体育竞技模板&#xff0c;总有一款适合您 111个HTML体育竞技模板下载链接&#xff1a;https://pan.baidu.com/s/1JmjlNVApPLVWnSEN5VlILQ?pwd5kzl 提取码&#xff1a;5kzl Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 响应式户外攀岩运动宣传…

【亲测有效】错误代码0x904,远程桌面连接异常终端问题

【亲测有效】错误代码0x904&#xff0c;远程桌面连接异常终端问题一、问题情况二、解决办法1.打开防火墙&#xff0c;如图&#xff0c;点击“允许应用或功能通过Windows”2.点击“允许其他应用”3.点击“浏览”4.在如下目录找到“mstsc.exe”添加5.点击“添加”6.勾选“专用”和…

[软件工程导论(第六版)]第5章 总体设计(课后习题详解)

文章目录1. 为每种类型的模块耦合举一个具体例子。2. 为每种类型的模块内聚举一个具体例子。3. 用面向数据流的方法设计下列系统的软件结构。4. 美国某大学共有200名教师&#xff0c;校方与教师工会刚刚签订一项协议。按照协议&#xff0c;所有年工资超过$26000&#xff08;含$…

电商搜索和推荐场景下的MLOps实践

省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2023年1月份热门报告合集ChatGPT的发展历程、原理、技术架构及未来方向《ChatGPT&#xff1a;真格基金分享.pdf》2023年AIGC发展趋势报告&#xff1a;人工智能的下一时代2023年…

图的基本概念和性质

目录一、图的引入1.引言2.不同类型的图3.无序对和无序积4.什么是图二、图的表示1.图的表示2.邻接矩阵3.邻接点与邻接边4.一些简单的特殊图三、图的分类1.按边有无方向分类2.按有无平行边分类3.按有无权值分类4.综合分类方法四、子图和补图1.各类子图2.完全图3.补图4.补图的邻接…

WSL(ubuntu2204)xfce4安装中文环境和fcitx5框架及中文输入法

安装中文语言包 sudo apt install language-pack-zh-hans 配置中文显示 首先运行如下命令配置 locale $ sudo vi /etc/locale.gen 找到 *zh_CN.UTF-8 UTF-8* 并取消注释&#xff0c;然后保存并退出。 locale.gen... # zh_CN.GBK GBK zh_CN.UTF-8 UTF-8 # zh_HK BIG5-HKS…

【C语言刷题】倒置字符串

解题思路与过程&#x1f4fd;️解题思路&#x1f4fd;️解题过程&#x1f527;1.输入&#x1f527;2.设计逆序函数&#x1f527;3.逆序整个字符串&#x1f527;4.逆序每个单词&#x1f4fd;️源码&#x1f4f7;先来看题&#x1f447;&#x1f4fd;️解题思路 &#x1f534; 首先…

【验证码的识别】—— 图形验证码的识别

前言 &#xff08;结尾有彩蛋欧&#xff09; 目前&#xff0c;许多网站采取各种各样的措施来反爬虫&#xff0c;其中一个措施便是使用验证码。随着技术的发展&#xff0c;验证码的花样越来越多。验证码最初是几个数字组合的简单的图形验证码&#xff0c;后来加入了英文字母和混…

RK356x U-Boot研究所(命令篇)3.6 fdt命令的用法

平台U-Boot 版本Linux SDK 版本RK356x2017.09v1.2.3文章目录 一、fdt命令的配置二、fdt命令的定义三、fdt命令的用法3.1 fdt list3.2 fdt rm3.3 fdt set一、fdt命令的配置 .config配置文件需要有以下配置: rk3568_defconfig默认已使能。 二、fdt命令的定义 usb命令定义在cm…

169、【动态规划】leetcode ——123. 买卖股票的最佳时机 III:二维数组+一维数组 (C++版本)

题目描述 原题链接&#xff1a;123. 买卖股票的最佳时机 III 解题思路 &#xff08;1&#xff09;二维dp数组 动态规划五步曲&#xff1a; &#xff08;1&#xff09;dp数组含义&#xff1a; dp[i][0]&#xff0c;表示无操作。主要由四个状态来表示四种操作。dp[i][1]&…

博客系统web自动化测试

目录 一、项目简介 二、测试用例 三、测试过程 3.1 环境搭建 3.2 编写代码 3.2.1 博客登陆页面测试 3.2.2 博客列表页面测试 3.2.3 博客详情页面测试 3.2.4 博客编辑页面测试 四、测试评估 一、项目简介 本项目是一个简易的个人博客系统&#xff0c;用户可以在登陆后…

W800开发板|SDK| HLK-W800-KIT-PRO|工具链|本地命令行编译|Windows11|WSL|(5)、海凌科W800开发板命令行编译指南

概况 海凌科W800开发板&#xff08;HLK-W800-KIT&#xff09;是海凌科电子面向开发者针对联盛德W800 芯片推出的一款多功能开发板。全功能版本与联盛德W800开发板相比&#xff0c;增加了温湿度传感器、RGB 灯以及音频解码器、功放可以直接在开发版验证功能。联盛德W800的csdk也…

java虚拟机内存分布

java虚拟机内存分布 Java虚拟机在执行java程序的过程中会把它所管理的内存划分为如下若干个不同的数据区域。 1.程序计数器 程序计数器是线程私有的&#xff0c;它占用的空间相对较小&#xff0c;用来记录当前线程字节码执行到哪一步。字节码解释器通过改变这个计数器的值来获…