zoukankan      html  css  js  c++  java
  • 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的初始化结束了。

      

      

  • 相关阅读:
    线程安全-003-对象锁的同步和异步
    线程安全-002-多个线程多把锁&类锁
    线程安全-001
    FastDFS单节点安装
    Nginx+Keepalived 实现高可用
    linux下配置nginx负载均衡例子
    linux下配置nginx反向代理例子
    Linux命令
    nginx配置文件 nginx.conf 说明
    CentOS安装Nginx 以及日志管理
  • 原文地址:https://www.cnblogs.com/vincentren/p/15685730.html
Copyright © 2011-2022 走看看