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

这篇具有很好参考价值的文章主要介绍了SpringSecurity源码分析(一) SpringBoot集成SpringSecurity即Spring安全框架的加载过程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

      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类型的过滤器。

[org/springframework/boot/autoconfigure/security/servlet/websecurityenablerc,SpringSecurity,spring boot,spring,java

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

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

    在springboot启动过程中会获取spring.factories配置文件里的配置类并加载到spring容器中,观察spring.factories配置文件里的配置内容,涉及到springsecurity的如下图红框处所示。[org/springframework/boot/autoconfigure/security/servlet/websecurityenablerc,SpringSecurity,spring boot,spring,java

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

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

[org/springframework/boot/autoconfigure/security/servlet/websecurityenablerc,SpringSecurity,spring boot,spring,java

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容器会调用该方法。

[org/springframework/boot/autoconfigure/security/servlet/websecurityenablerc,SpringSecurity,spring boot,spring,java在onStartUp方法中存在register,根据动态绑定机制执行的是DynamicRegistrationBean里的register方法。

	@Override
	protected final void register(String description, ServletContext servletContext) {
        //重点关注
		D registration = addRegistration(description, servletContext);
		if (registration == null) {
			logger.info(StringUtils.capitalize(description) + " was not registered "
					+ "(possibly already registered?)");
			return;
		}
        //重点关注
		configure(registration);
	}

该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类型
            //返回一个ApplicationFilterRegistration类型的对象
            
            return context.addFilter(filterName, filter);
        }
    }
    @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 {
            return context.addFilter(filterName, filter);
        }
    }

其addFilter方法最终调用了以下ApplicationContext类里addFilter方法,该方法返回了一个

ApplicationFilterRegistration类型的对象。该对象的构造参数分别是filterDef和context

    private FilterRegistration.Dynamic addFilter(String filterName,
            String filterClass, Filter filter) throws IllegalStateException {

        if (filterName == null || filterName.equals("")) {
            throw new IllegalArgumentException(sm.getString(
                    "applicationContext.invalidFilterName", filterName));
        }

        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            //TODO Spec breaking enhancement to ignore this restriction
            throw new IllegalStateException(
                    sm.getString("applicationContext.addFilter.ise",
                            getContextPath()));
        }

        FilterDef filterDef = context.findFilterDef(filterName);

        // Assume a 'complete' FilterRegistration is one that has a class and
        // a name
        if (filterDef == null) {
            filterDef = new FilterDef();
            filterDef.setFilterName(filterName);
            context.addFilterDef(filterDef);
        } else {
            if (filterDef.getFilterName() != null &&
                    filterDef.getFilterClass() != null) {
                return null;
            }
        }

        if (filter == null) {
            filterDef.setFilterClass(filterClass);
        } else {
            filterDef.setFilterClass(filter.getClass().getName());
            filterDef.setFilter(filter);
        }
        //重点关注此处,filterDef filtername是springSecurityFilterChain,该属性后面configure方法会使用

        return new ApplicationFilterRegistration(filterDef, context);
    }

继续回到DynamicRegistrationBean里的register方法。该方法里调用了configure方法,虽然因为调用onStartup方法的对象是DelegatingFilterProxyRegistrationBean,所以虽然DynamicRegistrationBean里有configure方法,实际上其调用的是AbstractFilterRegistrationBean里的register方法。该方法最终返回个ApplicationFilterRegistration类型的对象。

在configure方法中通过debug发现其通过registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter, DEFAULT_URL_MAPPINGS);

	protected void configure(FilterRegistration.Dynamic registration) {
		super.configure(registration);
		EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;
		if (dispatcherTypes == null) {
			dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
		}
		Set<String> servletNames = new LinkedHashSet<>();
		for (ServletRegistrationBean<?> servletRegistrationBean : this.servletRegistrationBeans) {
			servletNames.add(servletRegistrationBean.getServletName());
		}
		servletNames.addAll(this.servletNames);
		if (servletNames.isEmpty() && this.urlPatterns.isEmpty()) {
			this.logger.info("Mapping filter: '" + registration.getName() + "' to: "
					+ Arrays.asList(DEFAULT_URL_MAPPINGS));
            //重点关注此处
			registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
					DEFAULT_URL_MAPPINGS);
		}
		else {
			if (!servletNames.isEmpty()) {
				this.logger.info("Mapping filter: '" + registration.getName()
						+ "' to servlets: " + servletNames);
				registration.addMappingForServletNames(dispatcherTypes, this.matchAfter,
						StringUtils.toStringArray(servletNames));
			}
			if (!this.urlPatterns.isEmpty()) {
				this.logger.info("Mapping filter: '" + registration.getName()
						+ "' to urls: " + this.urlPatterns);
				registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
						StringUtils.toStringArray(this.urlPatterns));
			}
		}
	}

 对TomcatEmbeddedContext类的filterMap属性进行赋值供后面调用。filterMap里放入的FilterName是springSecurityFilterChain。文章来源地址https://www.toymoban.com/news/detail-780355.html

    public void addMappingForUrlPatterns(
            EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
            String... urlPatterns) {

        FilterMap filterMap = new FilterMap();
        //filterDef 是 ApplicationContext里赋值的

        filterMap.setFilterName(filterDef.getFilterName());

        if (dispatcherTypes != null) {
            for (DispatcherType dispatcherType : dispatcherTypes) {
                filterMap.setDispatcher(dispatcherType.name());
            }
        }

        if (urlPatterns != null) {
            // % decoded (if necessary) using UTF-8
            for (String urlPattern : urlPatterns) {
                filterMap.addURLPattern(urlPattern);
            }

            if (isMatchAfter) {
                context.addFilterMap(filterMap);
            } else {
                context.addFilterMapBefore(filterMap);
            }
        }
        // else error?

    }

到了这里,关于SpringSecurity源码分析(一) SpringBoot集成SpringSecurity即Spring安全框架的加载过程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【框架源码】SpringBoot核心源码解读之启动类源码分析

    首先我们要先带着我们的疑问,spring boot是如何启动应用程序?去分析SpringBoot的启动源码。 我们在新建SpringBoot项目时,核心方法就是主类的run方法。 SpringApplication.run(ArchWebApplication.class, args) 我们点击run方法进入到源码中,这块传入的了一个我们当前程序主类的类对象以及主

    2024年02月06日
    浏览(28)
  • 权限管理 springboot集成springSecurity Oauth2 JWT

    目录 一、SpringSeurity的基础操作 1、引入主要依赖 2、加密器 3、实现自定义登录逻辑 4、访问限制 5、自定义异常处理  6、通过注解的方式配置访问控制 二、Auth2认证方案 1、什么是Auth2认证 2、Oauth2最常用的授权模式  3、依赖引入 4、添加配置类 5、测试 6、存在到Redis里,后续

    2023年04月14日
    浏览(32)
  • SpringSecurity安全框架简介

    Spring Security是Spring全家桶的成员,官方对它的介绍是: 从介绍里可以看出,Spring Security是一个可定制扩展的框架,它主要提供了身份验证和访问控制功能。而这两个功能也是基于框架的扩展机制开发的,下面让我们一起了解一下Spring Security的基本概念和扩展机制的实现原理。

    2024年02月08日
    浏览(30)
  • SpringSecurity 安全框架详解

    先赘述一下身份认证和用户授权: 用户认证( Authentication ):系统通过校验用户提供的用户名和密码来验证该用户是否为系统中的合法主体,即是否可以访问该系统; 用户授权( Authorization ):系统为用户分配不同的角色,以获取对应的权限,即验证该用户是否有权限执行

    2024年02月02日
    浏览(30)
  • SpringSecurity安全框架

    我们使用这个springSecurity安全框架,作用是认证,授权,将 用户的权限和对应的资源进行绑定 , 默认的是在内存中保存的,实际开发中,是需要根据项目业务的需求 对某些方法进行重写, 使数据库中权限对应的资源进行绑定, 就是查看当前登录的用户所扮演的角色,该角色有哪些权限

    2024年02月22日
    浏览(24)
  • SpringBoot原理分析 | 安全框架:Security

    💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架;提供一组可以在Spring应用上下文中配置的Bean,充分利用Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection

    2024年02月13日
    浏览(78)
  • 【SpringBoot】简介及传统的 Spring 框架:对比和分析

     哈喽,哈喽,大家好~ 我是你们的老朋友: 保护小周ღ    今天给大家带来的是 SpringBoot 的简介,SpringBoot 项目的创建,相较于 Spring 框架的优点: 1. 快速的集成框架  2.内置运行容器, 快速的部署项目 3. 摒弃繁琐的 xml,使用注解和配置的方式进行开发。4. 支持更多的监控

    2024年02月15日
    浏览(26)
  • SpringSecurity分布式安全框架

    Spring Security是一个基于Spring框架的安全框架,它提供了全面的安全解决方案,包括用户认证和用户授权等Web应用安全性问题。Spring Security可以轻松扩展以满足自定义需求,它的真正强大之处在于它可以轻松扩展以满足自定义要求。 对于分布式系统来说,Spring Security可以结合

    2024年02月08日
    浏览(35)
  • SpringSecurity安全框架 ——认证与授权

    目录  一、简介 1.1 什么是Spring Security 1.2 工作原理 1.3 为什么选择Spring Security 1.4 HttpSecurity 介绍🌟 二、用户认证 2.1 导入依赖与配置 2.2 用户对象UserDetails 2.3 业务对象UserDetailsService 2.4 SecurityConfig配置 2.4.1 BCryptPasswordEncoder密码编码器 2.4.2 RememberMe 记住登录信息 2.4.3 CSR

    2024年02月04日
    浏览(33)
  • springcloud/springboot集成NACOS 做注册和配置中心以及nacos源码分析

    Spring Cloud 是一系列框架的有序集合如服务发现注册、配置中心、消息总线、负载均衡、熔断器、数据监控等。 SpringCloud 将多个服务框架组合起来,通过Spring Boot进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者提供了一套简单易懂、易部署和易维护的分布式系统开

    2024年02月08日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包