spring security 源碼學習(三)WebSecurityConfiguration


本篇目標是解析WebSecurityConfiguration是如何初始化的

首先,看下他的源碼。

/**
 * 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}.
 *
 * @see EnableWebSecurity
 * @see WebSecurity
 *
 * @author Rob Winch
 * @author Keesun Baik
 * @since 3.2
 */
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
	private WebSecurity webSecurity;

	private Boolean debugEnabled;

	private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;

	private ClassLoader beanClassLoader;

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

	@Bean
	public static DelegatingApplicationListener delegatingApplicationListener() {
		return new DelegatingApplicationListener();
	}

	@Bean
	@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
		return webSecurity.getExpressionHandler();
	}

	/**
	 * Creates the Spring Security Filter Chain
	 * @return
	 * @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();
	}

	/**
	 * 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();
	}

	/**
	 * Sets the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>}
	 * instances used to create the web configuration.
	 *
	 * @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;
	}

	@Bean
	public 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;
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @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;
	}
}

  如這個類開頭的注釋寫的一樣,這個類的最后會生成一個FilterChainProxy類(一個Fliter),作為過濾器(鏈)來處理一個請求進入spring后進行的認證操作。我們挑着這里面比較重要的步驟進行分析下。

(一)AutowiredWebSecurityConfigurersIgnoreParents初始化

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

 這個類有一個獲取所有WebSecurityConfigurer子類實例的方法

	public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
		List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
		Map<String, WebSecurityConfigurer> beansOfType = beanFactory
				.getBeansOfType(WebSecurityConfigurer.class);
		for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
			webSecurityConfigurers.add(entry.getValue());
		}
		return webSecurityConfigurers;
	}

 這個類的用途在下面

(二)WebSecurity實例化以及基本配置的設置

	@Autowired(required = false)
	public void setFilterChainProxySecurityConfigurer(
			ObjectPostProcessor<Object> objectPostProcessor,
               //利用上面的初始化的AutowiredWebSecurityConfigurersIgnoreParents的getWebSecurityConfigurers方法獲取我們的Spring security的配置 @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
          //創建WebSecurity實例 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; }

  這個方法主要的操作是實例化WebSecurity,並將Spring security的配置設置為他的webSecurityConfigurers屬性的值

    這邊為了方便查看之前的配置,我們把配置類的代碼也插入到這邊

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
				.antMatchers("/", "/home").permitAll()
				.anyRequest().authenticated()
				.and()
			.formLogin()
				.loginPage("/login")
				.permitAll()
				.and()
			.logout()
				.permitAll();
	}

	@Bean
	@Override
	public UserDetailsService userDetailsService() {
		UserDetails user =
			 User.withDefaultPasswordEncoder()
				.username("user")
				.password("password")
				.roles("USER")
				.build();

		return new InMemoryUserDetailsManager(user);
	}
}

  (三)springSecurityFilterChain初始化

	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
          //step 1 boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty(); if (!hasConfigurers) { WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { }); webSecurity.apply(adapter); }
          //step 2 return webSecurity.build(); }

  就是這個方法,將最核心的Spring security的過濾器(鏈)初始化了。

  這個方法的第一步就是判斷當前是否配置了webSecurityConfigurers,如果沒有,則會生成一個默認的:new WebSecurityConfigurerAdapter(),這個也就可以解釋我剛接觸Spring security時的困惑(為什么引入了Spring security的依賴后,我基本的配置類沒寫,我的接口就不能調用了?答:這邊給了個默認的配置)

  第二步就會對webSecurity進行構建:webSecurity.build();

  首先調用的是AbstractSecurityBuilder的build()方法

	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");
	}

  然后調用doBuild()方法,doBuild()是在AbstractSecurityBuilder的子類AbstractConfiguredSecurityBuilder中實現的

	@Override
	protected final O doBuild() throws Exception {
		synchronized (configurers) {
			buildState = BuildState.INITIALIZING;

			beforeInit();
			init();

			buildState = BuildState.CONFIGURING;

			beforeConfigure();
			configure();

			buildState = BuildState.BUILDING;

			O result = performBuild();

			buildState = BuildState.BUILT;

			return result;
		}
	}

  這是一個很典型的模版方法模式,其中的beforeInit()和beforeConfigure()皆為鈎子方法,這里默認也沒有任何實現,我們暫時不用關注,主要需要注意的是init()、configure()和performBuild()方法。下面我們一個個分析這幾個方法。

1、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);
		}
	}

  這邊的configurer.init((B) this);調用的是WebSecurityConfigurerAdapter的init方法,源碼如下

	public void init(final WebSecurity web) throws Exception {
		final HttpSecurity http = getHttp();(1)
		web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
			public void run() {
				FilterSecurityInterceptor securityInterceptor = http
						.getSharedObject(FilterSecurityInterceptor.class);
				web.securityInterceptor(securityInterceptor);
			}
		});(2)
	}

  第(1)步是初始化了HttpSecurity,第(2)步的前半部分web.addSecurityFilterChainBuilder(http)則將這個HttpSecurity加入到了webSecurity的

	private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();

  這個屬性中,后面performBuild的時候會用到;

  這里的第(1)步其實很關鍵,一下子初始化了2個核心。這里再多說一句,針對不同的WebSecurityConfigurerAdapter的實現類,也就是配置,他們各自的HttpSecurity是各自的。

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

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

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

		http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
				sharedObjects);(2)
		if (!disableDefaults) {
			// @formatter:off
			http
				.csrf().and()(3)
				.addFilter(new WebAsyncManagerIntegrationFilter())(4)
				.exceptionHandling().and()(5)
				.headers().and()(6)
				.sessionManagement().and()(7)
				.securityContext().and()(8)
				.requestCache().and()(9)
				.anonymous().and()(10)
				.servletApi().and()(11)
				.apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and()(12)
				.logout();(13)
			// @formatter:on
			ClassLoader classLoader = this.context.getClassLoader();
			List<AbstractHttpConfigurer> defaultHttpConfigurers =
					SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

			for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
				http.apply(configurer);
			}
		}
		configure(http);
		return http;
	}

  這個方法的第(1)步獲取了AuthenticationManager,而如果AuthenticationManager沒有被初始化,則這里則對AuthenticationManager進行了初始化,后面的第(2)步則實例化了HttpSecurity,第(3)-(13)步則給HttpSecurity加了些configure和給WebSecurity加了些Filter。下面我們着重看第(1)步,后面的幾步會在后面的不同Filter的文章中分別交代。下面看下authenticationManager()方法

	protected AuthenticationManager authenticationManager() throws Exception {
		if (!authenticationManagerInitialized) {
              //提供了自定義配置的地方,如果在我們的配置類中override了configure(AuthenticationManagerBuilder auth)方法,則就是在這里生效的 configure(localConfigureAuthenticationBldr); if (disableLocalConfigureAuthenticationBldr) {
                    //提供了默認的配置AuthenticationManager的方法,一般我們什么都不配置,則走的這里的方法 authenticationManager = authenticationConfiguration .getAuthenticationManager(); } else { authenticationManager = localConfigureAuthenticationBldr.build(); } authenticationManagerInitialized = true; } return authenticationManager; }

  這個方法的作用寫在了注釋中,我們繼續看authenticationConfiguration.getAuthenticationManager();方法

public AuthenticationManager getAuthenticationManager() throws Exception {
		if (this.authenticationManagerInitialized) {
			return this.authenticationManager;
		}
          //new了一個AuthenticationManagerBuilder實例,執行了new AuthenticationManagerBuilder(objectPostProcessor) AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder( this.objectPostProcessor); if (this.buildingAuthenticationManager.getAndSet(true)) { return new AuthenticationManagerDelegator(authBuilder); }            for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {(1) authBuilder.apply(config); }            authenticationManager = authBuilder.build();(2) if (authenticationManager == null) { authenticationManager = getAuthenticationManagerBean(); } this.authenticationManagerInitialized = true; return authenticationManager; }

  這邊核心的有2步,第(1)步是獲取配置,第(2)步是我們熟悉的初始化節奏。而第一步的配置,默認有如下的配置,他們具體從哪來的,我們留到后面再講解。

 

 

 那么第(2)步就來到了AuthenticationManagerBuilder的build(),繼而就是doBuild(),也就是我們上面的模版方法,這邊我們再貼一次代碼

protected final O doBuild() throws Exception {
		synchronized (configurers) {
			buildState = BuildState.INITIALIZING;

			beforeInit();
			init();

			buildState = BuildState.CONFIGURING;

			beforeConfigure();
			configure();

			buildState = BuildState.BUILDING;

			O result = performBuild();

			buildState = BuildState.BUILT;

			return result;
		}
	}

  這邊最終返回的是ProviderManager,上面的init和configur我們先跳過分析,直接看這邊的performBuild()

	protected ProviderManager performBuild() throws Exception {
		if (!isConfigured()) {
			logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
			return null;
		}
		ProviderManager providerManager = new ProviderManager(authenticationProviders,
				parentAuthenticationManager);
		if (eraseCredentials != null) {
			providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
		}
		if (eventPublisher != null) {
			providerManager.setAuthenticationEventPublisher(eventPublisher);
		}
		providerManager = postProcess(providerManager);
		return providerManager;
	}

  這邊可以看的出來ProviderManager是new出來的,而他最重要的則是后面的參數authenticationProviders,也就是List<AuthenticationProvider> authenticationProviders,他就是進行鑒權的核心實現。

 AuthenticationManager的初始化,我們就先到這里,后面會有一篇文章再對這個過程進行詳細梳理。

2、configure(WebSecurity web)方法

這邊是對WebSecurity進行定制化的地方,這個方法相對init()來說就簡單多了

默認來講,如果我們在我們的配置類中未override這個方法,那么這里將什么都不發生,默認實現如下。

	public void configure(WebSecurity web) throws Exception {
	}

這里我們可以配置一些需要忽略的,也就是不需要認證的請求,如:

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/test/**");
    }

  這邊的配置和下面的ignoredRequests.size()相對應。

 

3、performBuild() 

這個方法將會真正的實例化Filter並返回

@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<SecurityFilterChain>(
        chainSize);
    for (RequestMatcher ignoredRequest : ignoredRequests) {
      securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
    }
// for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build());(1) } FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);(2) 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); } postBuildAction.run(); return result; }

這里的第(1)步則是實例化SecurityFilterChain,加入到List<SecurityFilterChain> filterChains中,這里的securityFilterChainBuilder.build()實質上就是HttpSecurity進行build,這里的list中有多少值就看系統配置了多少個WebSecurityConfigurerAdapter,按照我們之前的配置,這里就一個。根據下面的類圖,也就是調用的AbstractSecurityBuilder的build()方法

 

 

 HttpSecurity的build我們發到下一篇文章。

第(2)步則將List<SecurityFilterChain> filterChains作為FilterChainProxy初始化的屬性,初始化了FilterChainProxy,並返回了這個Filter。

至此,FilterChainProxy的初始化結束了,也就是Spring security的初始化結束了。

  

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM