Spring Security Config : WebSecurityConfigurerAdapter

概述

介绍

WebSecurityConfigurerAdapterSpring Security Config内置提供的一个WebSecurityConfigurer抽象实现类。WebSecurityConfigurerAdapter存在的目的是提供一个方便开发人员配置WebSecurity的基类。它提供了一组全方位配置WebSecurity的缺省方法实现。开发人员只要继承WebSecurityConfigurerAdapter提供自己的实现类,哪怕不覆盖WebSecurityConfigurerAdapter的任何一个方法,都得到了一个配置WebSecurity的安全配置器WebSecurityConfigurer实例。但通常情况下,开发人员都有自己特定的安全配置和要求,这时候就可以在自己提供的WebSecurityConfigurerAdapter子实现类中提供自己的方法覆盖WebSecurityConfigurerAdapter相应的方法从而对WebSecurity实施定制。

WebSecurityConfigurerAdapter为开发人员提供了如下功能 :

  1. 构建一个用于配置WebSecurityWebSecurityConfigurerAdapter对象,可以指定使用或者不使用缺省配置,默认构造函数使用缺省配置;
  2. 提供可覆盖实现的方法void configure(AuthenticationManagerBuilder auth) ,允许开发人员配置目标WebSecurity所使用的AuthenticationManager的双亲AuthenticationManager;该方法缺省实现的效果是该双亲AuthenticationManager来自AuthenticationConfiguration定义的AuthenticationManager(其实是来自IoC容器的类型为AuthenticationManager的一个bean);

    通过覆盖实现该方法,开发人员可以定制认证机制,比如设置成基于内存的认证,基于数据库的认证,基于LDAP的认证,甚至这些认证机制的一个组合,设置AuthenticationManager的双亲关系,所使用的PasswordEncoder等等;

  3. 提供可覆盖实现的空方法void configure(WebSecurity web),允许开发人员覆盖实现配置WebSecurity,比如设置哪些URL要忽略安全等等;

    通过覆盖实现该方法,开发人员可以定制WebSecurity,主要是除了HttpSecurity之外的安全控制,比如忽略某些静态公开资源或者动态公开资源的安全 ,设置需要使用的防火墙实例,设置权限评估器,安全表达式处理器等;

  4. WebSecurityConfigurerAdapter自身是一个WebSecurityConfigurer,它在自己的初始化方法init()中创建了HttpSecurity http安全构建器对象,并在缺省情况下(disableDefaultsfalse)应用了如下HttpSecurity初始配置:
			http
				.csrf().and() // 应用 CsrfConfigurer
				.addFilter(new WebAsyncManagerIntegrationFilter()) // 添加过滤器 WebAsyncManagerIntegrationFilter
				.exceptionHandling().and() // 应用 ExceptionHandlingConfigurer
				.headers().and() // 应用 HeadersConfigurer
				.sessionManagement().and() // 应用 SessionManagementConfigurer
				.securityContext().and() // 应用 SecurityContextConfigurer
				.requestCache().and() // 应用 RequestCacheConfigurer
				.anonymous().and() // 应用 AnonymousConfigurer
				.servletApi().and() // 应用 ServletApiConfigurer
				.apply(new DefaultLoginPageConfigurer<>()).and() // 应用 DefaultLoginPageConfigurer
				.logout(); // 应用 LogoutConfigurer

			// 使用 SpringFactoriesLoader 加载 classpath 上所有jar包中各自的 META-INF/spring.factories 属性文件
			// 中指定的 AbstractHttpConfigurer,应用到 http
			ClassLoader classLoader = this.context.getClassLoader();
			List<AbstractHttpConfigurer> defaultHttpConfigurers =
					SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

			for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
				http.apply(configurer);
			}
  1. 提供可覆盖实现的空方法void configure(HttpSecurity http),允许开发人员配置目标HttpSecurity;这里该方法缺省的实现对HttpSecurity的安全配置如下:
    1. 对任何请求要求用户已认证(通俗地讲,用户必须先登录才能访问任何资源);
    2. 启用用户名密码表单登录认证机制;
    3. 启用Http Basic认证机制;

    通过覆盖实现该方法,开发人员可以定制HttpSecurity;

		http
			.authorizeRequests().anyRequest().authenticated().and() 
			// 上面行应用一个 ExpressionUrlAuthorizationConfigurer,要求所有URL必须登录认证后才能访问
			.formLogin().and() // 应用 FormLoginConfigurer
			.httpBasic(); // 应用 HttpBasicConfigurer

继承关系

WebSecurityConfigurerAdapter

使用

  • 使用例子 1 – 提供子类实现,然会作为配置类使用 :
package org.springframework.boot.autoconfigure.security.servlet;
// 省略 imports
@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {

	@Configuration
	@Order(SecurityProperties.BASIC_AUTH_ORDER)
	static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {

	}

}

开发人员通常使用类似这种方式配置WebSecurity

  • 使用例子 2 – 提供匿名实现类并直接实例化和应用 :
// 配置类 WebSecurityConfiguration 代码片段
	@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();
	}

源代码

源代码版本 : 5.1.4.RELEASE

package org.springframework.security.config.annotation.web.configuration;

// 省略 imports


@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
		WebSecurityConfigurer<WebSecurity> {
	private final Log logger = LogFactory.getLog(WebSecurityConfigurerAdapter.class);

	private ApplicationContext context;

	private ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();

    // 在每个安全对象创建之后需要执行后置动作的 后置动作处理器,这里的缺省值
    // 其实只是抛出异常声明IoC容器中必须存在一个ObjectPostProcessor bean:
    // 参考 @EnableWebSecurity => @EnableGlobalAuthentication
    //       => AuthenticationConfiguration => ObjectPostProcessorConfiguration
	private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
		public <T> T postProcess(T object) {
			throw new IllegalStateException(
					ObjectPostProcessor.class.getName()
							+ " is a required bean. Ensure you have used @EnableWebSecurity and @Configuration");
		}
	};

    // 配置 WebSecurity 需要使用到的认证配置,可以认为是全局认证配置,会通过 set 方法被自动注入,
    // 该属性会用于从IoC容器获取目标 WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager 的双亲
    // AuthenticationManager 。 该方式可能用得上,也可能用不上,要看开发人员是配置使用 
    // localConfigureAuthenticationBldr 还是使用该属性用于构建目标 WebSecurity/HttpSecurity 所要直接使用的 
    // AuthenticationManager 的双亲 AuthenticationManager。
	private AuthenticationConfiguration authenticationConfiguration;
    // AuthenticationManager 构建器,缺省使用 : DefaultPasswordEncoderAuthenticationManagerBuilder
    // 所有构建的 AuthenticationManager 会是目标 WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager
	private AuthenticationManagerBuilder authenticationBuilder;
     // AuthenticationManager 构建器,缺省使用 : DefaultPasswordEncoderAuthenticationManagerBuilder
     // 所要构建的 AuthenticationManagerBuilder 会是目标 WebSecurity/HttpSecurity 所要直接使用的 
     // AuthenticationManager 的双亲 AuthenticationManager。 不过缺省情况下,也就是开发人员不在子类
     // 中覆盖实现 void configure(AuthenticationManagerBuilder auth) 的情况下, 该 localConfigureAuthenticationBldr
     // 不会被用于构建目标 WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager 的双亲 
     // AuthenticationManager, 这种情况下的双亲 AuthenticationManager 会来自 authenticationConfiguration
	private AuthenticationManagerBuilder localConfigureAuthenticationBldr;
    // 是否禁用 localConfigureAuthenticationBldr, 缺省情况下,也就是开发人员不在子类中覆盖实现 
    // void configure(AuthenticationManagerBuilder auth) 的情况下,  当前 WebSecurityConfigurerAdapter
    // 缺省提供的 void configure(AuthenticationManagerBuilder auth)  方法实现会将该标志设置为 true,
    // 也就是不使用 localConfigureAuthenticationBldr 构建目标 WebSecurity/HttpSecurity 所要直接使用的
    // AuthenticationManager 的双亲 AuthenticationManager, 而是使用 authenticationConfiguration
    // 提供的 AuthenticationManager 作为 双亲 AuthenticationManager。
	private boolean disableLocalConfigureAuthenticationBldr;
    // 标志属性 : 目标 WebSecurity/HttpSecurity 所要直接使用的AuthenticationManager的双亲 authenticationManager
    // 是否已经初始化
	private boolean authenticationManagerInitialized;
    // 目标 WebSecurity/HttpSecurity 所要直接使用的AuthenticationManager的双亲 authenticationManager
	private AuthenticationManager authenticationManager;
    // 根据传入的 Authentication 的类型判断一个 Authentication 是否可被信任,
    // 缺省使用实现机制 AuthenticationTrustResolverImpl
    // 可被设置
	private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
    // HTTP 安全构建器,用于配置匹配特定URL模式的控制器方法的安全,构建产物是 DefaultSecurityFilterChain
	private HttpSecurity http;
    // 是否禁用缺省配置,缺省为 false,可以通过当前类构造函数设置为true
	private boolean disableDefaults;

	/**
	 * Creates an instance with the default configuration enabled.
     * 缺省构造函数, 缺省配置机制启用 : disableDefaults == false
	 */
	protected WebSecurityConfigurerAdapter() {
		this(false);
	}

	/**
	 * Creates an instance which allows specifying if the default configuration should be
	 * enabled. Disabling the default configuration should be considered more advanced
	 * usage as it requires more understanding of how the framework is implemented.
	 *
	 * @param disableDefaults true if the default configuration should be disabled, else
	 * false
	 */
	protected WebSecurityConfigurerAdapter(boolean disableDefaults) {
		this.disableDefaults = disableDefaults;
	}

	/**
	 * 开发人员可以覆盖该方法用于配置指定的 AuthenticationManagerBuilder auth,
	 * 如果开发人员这么做了,那么这里所被配置的 auth , 其实就是当前配置器的属性
	 * localConfigureAuthenticationBldr 会被用于构建 WebSecurity/HttpSecurity
	 * 所要使用的 AuthenticationManager 的双亲 AuthenticationManager。 
	 * 如果开发人员不覆盖实现此方法,此缺省实现其实只是设置一个禁用标志,禁用
	 * localConfigureAuthenticationBldr, 此时 WebSecurity/HttpSecurity 所要使
	 * 用的 AuthenticationManager 的双亲 AuthenticationManager 将会来自 
	 * authenticationConfiguration.getAuthenticationManager()
	 * @param auth the AuthenticationManagerBuilder to use
	 * @throws Exception
	 */
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		this.disableLocalConfigureAuthenticationBldr = true;
	}

	/**
	 * Creates the HttpSecurity or returns the current instance
	 * 创建或者返回已经创建的 HttpSecurity 实例 
	 * 当前 WebSecurityConfigurer 只会创建一个 HttpSecurity 实例 ,
	 * 如果已经创建,该方法会返回已经创建了的 HttpSecurity 实例 ,而不会再创建一个
	 * @return the  HttpSecurity
	 * @throws Exception
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected final HttpSecurity getHttp() throws Exception {
		if (http != null) {
			// HttpSecurity 实例已经存在,直接返回使用
			return http;
		}

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

		// 获取 WebSecurity/HttpSecurity 所要直接使用的  AuthenticationManager
		// 的双亲 AuthenticationManager  
		AuthenticationManager authenticationManager = authenticationManager();
		//  authenticationBuilder 所要构建的目标 AuthenticationManager 才是
		// 当前配置器所配置的 WebSecurity/HttpSecurity 所要直接使用的  AuthenticationManager
		authenticationBuilder.parentAuthenticationManager(authenticationManager);
		authenticationBuilder.authenticationEventPublisher(eventPublisher);
        
		// 创建共享对象  
		Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();

		// 创建  HttpSecurity 实例
		http = new HttpSecurity(objectPostProcessor, authenticationBuilder, sharedObjects);
		if (!disableDefaults) {
			// HttpSecurity http 的缺省配置逻辑 ====>
			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();
                
			ClassLoader classLoader = this.context.getClassLoader();
			// 使用 SpringFactoriesLoader 加载 classpath 上所有jar包中各自的 META-INF/spring.factories 属性文件
			// 中指定的 AbstractHttpConfigurer,应用到 http
			List<AbstractHttpConfigurer> defaultHttpConfigurers =
					SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

			for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
				http.apply(configurer);
			}
			// HttpSecurity http 的缺省配置逻辑 <====
		}
		// 使用方法 protected void configure(HttpSecurity http) 配置 HttpSecurity http ,
		// 这里如果开发人员重写了该方法,则这里这些开发人员配置逻辑会被应用于配置 HttpSecurity http ,
		configure(http);
		return http;
	}

	/**
	 * Override this method to expose the AuthenticationManager from
	 * #configure(AuthenticationManagerBuilder) to be exposed as a Bean. For
	 * example:
	 *
	 * 
	 * @Bean(name name="myAuthenticationManager")
	 * @Override
	 * public AuthenticationManager authenticationManagerBean() throws Exception {
	 *     return super.authenticationManagerBean();
	 * }
	 * 
	 *
	 * @return the AuthenticationManager
	 * @throws Exception
	 */
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return new AuthenticationManagerDelegator(authenticationBuilder, context);
	}

	/**
	 * Gets the AuthenticationManager to use. The default strategy is if
	 *  #configure(AuthenticationManagerBuilder) method is overridden to use the
	 * AuthenticationManagerBuilder that was passed in. Otherwise, autowire the
	 * AuthenticationManager by type.
	 *
     * 获取构建 WebSecurity/HttpSecurity所要使用的 AuthenticationManager 的
     * 双亲 AuthenticationManager,这里的策略是 :
     * 1. 如果开发人员覆盖实现了 #configure(AuthenticationManagerBuilder) ,
     * 则会使用开发人员覆盖实现了的 AuthenticationManagerBuilder , 其实也就是
     * 当前配置器的 localConfigureAuthenticationBldr 构建一个 AuthenticationManager
     * 并返回和使用;
     * 2. 如果开发人员没有覆盖实现 #configure(AuthenticationManagerBuilder) ,
     * 则会使用  authenticationConfiguration#getAuthenticationManager() 提供的
     * AuthenticationManager, 这是从IoC容器中根据类型查找得到的一个 AuthenticationManager
	 * @return the AuthenticationManager to use
	 * @throws Exception
	 */
	protected AuthenticationManager authenticationManager() throws Exception {
		if (!authenticationManagerInitialized) {
			//  authenticationManager 尚未初始化的情况,在这里进行初始化
           
			// 调用 configure(AuthenticationManagerBuilder auth) 用于配置  localConfigureAuthenticationBldr,
			// 该方法有可能被开发人员覆盖实现
			configure(localConfigureAuthenticationBldr);
			if (disableLocalConfigureAuthenticationBldr) {
				// 如果开发人员没有覆盖实现 configure(AuthenticationManagerBuilder auth)
				// 方法, 则该方法的缺省实现会设置 disableLocalConfigureAuthenticationBldr=true,
				// 这种情况下会使用 authenticationConfiguration 获取IoC容器中配置的 AuthenticationManager
				// 作为目标WebSecurity/HttpSecurity 所要直接使用的 AuthenticationManager 的双亲
				authenticationManager = authenticationConfiguration
						.getAuthenticationManager();
			}
			else {
				// 如果开发人员覆盖实现了 configure(AuthenticationManagerBuilder auth)
				// 方法,则 localConfigureAuthenticationBldr 会被用于构建一个 AuthenticationManager,
				// 该 AuthenticationManager 会充当目标WebSecurity/HttpSecurity 所要直接使用的  
				// AuthenticationManager 的双亲            
				authenticationManager = localConfigureAuthenticationBldr.build();
			}
          //  authenticationManager 初始化完成的情况,设置相应标志  
			authenticationManagerInitialized = true;
		}
		return authenticationManager;
	}

	/**
	 * Override this method to expose a UserDetailsService created from
	 * #configure(AuthenticationManagerBuilder) as a bean. In general only the
	 * following override should be done of this method:
	 *
	 * 
	 * @Bean(name = "myUserDetailsService")
	 * // any or no name specified is allowed
	 * @Override
	 * public UserDetailsService userDetailsServiceBean() throws Exception {
	 * 	return super.userDetailsServiceBean();
	 * }
	 * 
	 *
	 * To change the instance returned, developers should change
	 * #userDetailsService() instead
	 * @return the UserDetailsService
	 * @throws Exception
	 * @see #userDetailsService()
	 */
	public UserDetailsService userDetailsServiceBean() throws Exception {
		AuthenticationManagerBuilder globalAuthBuilder = context
				.getBean(AuthenticationManagerBuilder.class);
		return new UserDetailsServiceDelegator(Arrays.asList(
				localConfigureAuthenticationBldr, globalAuthBuilder));
	}

	/**
	 * Allows modifying and accessing the UserDetailsService from
	 * #userDetailsServiceBean() without interacting with the
	 * ApplicationContext. Developers should override this method when changing
	 * the instance of #userDetailsServiceBean().
	 *
	 * @return the UserDetailsService to use
	 */
	protected UserDetailsService userDetailsService() {
		AuthenticationManagerBuilder globalAuthBuilder = context
				.getBean(AuthenticationManagerBuilder.class);
		return new UserDetailsServiceDelegator(Arrays.asList(
				localConfigureAuthenticationBldr, globalAuthBuilder));
	}

    // SecurityConfigurer 接口约定的初始化方法
	public void init(final WebSecurity web) throws Exception {
       // 创建  HttpSecurity http,这是一个 SecurityBuilder
		final HttpSecurity http = getHttp();
		web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
			public void run() {
				FilterSecurityInterceptor securityInterceptor = http
						.getSharedObject(FilterSecurityInterceptor.class);
				web.securityInterceptor(securityInterceptor);
			}
		});
	}

	/**
	 * Override this method to configure WebSecurity. For example, if you wish to
	 * ignore certain requests.
     * 实现子类需要覆盖实现此方法来自定义配置 WebSecurity
	 */
	public void configure(WebSecurity web) throws Exception {
	}

	/**
	 * Override this method to configure the HttpSecurity. Typically subclasses
	 * should not invoke this method by calling super as it may override their
	 * configuration. The default configuration is:
	 *
	 * 
	 * http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
	 * 
	 *
     * 实现子类需要覆盖实现此方法来自定义配置 HttpSecurity,
     * 这里的实现是一个缺省实现
	 * @param http the HttpSecurity to modify
	 * @throws Exception if an error occurs
	 */
	protected void configure(HttpSecurity http) throws Exception {
		logger.debug(
			"Using default configure(HttpSecurity). If subclassed this will potentially override "
			+	"subclass configure(HttpSecurity).");

		http
			.authorizeRequests()
				.anyRequest().authenticated()
				.and()
			.formLogin().and()
			.httpBasic();
	}

	/**
	 * Gets the ApplicationContext
	 * @return the context
	 */
	protected final ApplicationContext getApplicationContext() {
		return this.context;
	}

	@Autowired
	public void setApplicationContext(ApplicationContext context) {
		this.context = context;

		ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
		// 密码加密器,口令加密器,使用当前  WebSecurityConfigurerAdapter 的内部嵌套类 LazyPasswordEncoder
		LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);

		// 目标 WebSecurity/HttpSecurity 所要直接使用的  AuthenticationManager 的构建器
		authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, 
			passwordEncoder);
		// 目标 WebSecurity/HttpSecurity 所要直接使用的  AuthenticationManager  的双亲  AuthenticationManager 
		// 的构建器, 可能被用的上,也可能用不上,要看开发人员是否决定使用这个 localConfigureAuthenticationBldr
		localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(
			objectPostProcessor, passwordEncoder) {
			@Override
			public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
				authenticationBuilder.eraseCredentials(eraseCredentials);
				return super.eraseCredentials(eraseCredentials);
			}

		};
	}
    
    // 依赖注入 AuthenticationTrustResolver , 如果容器中有 AuthenticationTrustResolver bean
    // 则使用,否则则使用缺省值 : AuthenticationTrustResolverImpl
	@Autowired(required = false)
	public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
		this.trustResolver = trustResolver;
	}

    // 依赖注入 ContentNegotiationStrategy , 如果容器中有 ContentNegotiationStrategy bean
    // 则使用,否则则使用缺省值 : HeaderContentNegotiationStrategy
	@Autowired(required = false)
	public void setContentNegotationStrategy(
			ContentNegotiationStrategy contentNegotiationStrategy) {
		this.contentNegotiationStrategy = contentNegotiationStrategy;
	}

	@Autowired
	public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
		this.objectPostProcessor = objectPostProcessor;
	}

	@Autowired
	public void setAuthenticationConfiguration(
			AuthenticationConfiguration authenticationConfiguration) {
		this.authenticationConfiguration = authenticationConfiguration;
	}

	/**
	 * Creates the shared objects
	 *
	 * @return the shared Objects
	 */
	private Map<Class<? extends Object>, Object> createSharedObjects() {
		Map<Class<? extends Object>, Object> sharedObjects = new HashMap<Class<? extends Object>, Object>();
		sharedObjects.putAll(localConfigureAuthenticationBldr.getSharedObjects());
		sharedObjects.put(UserDetailsService.class, userDetailsService());
		sharedObjects.put(ApplicationContext.class, context);
		sharedObjects.put(ContentNegotiationStrategy.class, contentNegotiationStrategy);
		sharedObjects.put(AuthenticationTrustResolver.class, trustResolver);
		return sharedObjects;
	}

	/**
	 * Delays the use of the UserDetailsService from the
	 * AuthenticationManagerBuilder to ensure that it has been fully configured.
	 *
	 * @author Rob Winch
	 * @since 3.2
	 */
	static final class UserDetailsServiceDelegator implements UserDetailsService {
		private List<AuthenticationManagerBuilder> delegateBuilders;
		private UserDetailsService delegate;
		private final Object delegateMonitor = new Object();

		UserDetailsServiceDelegator(List<AuthenticationManagerBuilder> delegateBuilders) {
			if (delegateBuilders.contains(null)) {
				throw new IllegalArgumentException(
						"delegateBuilders cannot contain null values. Got "
								+ delegateBuilders);
			}
			this.delegateBuilders = delegateBuilders;
		}

		public UserDetails loadUserByUsername(String username)
				throws UsernameNotFoundException {
			if (delegate != null) {
				return delegate.loadUserByUsername(username);
			}

			synchronized (delegateMonitor) {
				if (delegate == null) {
					for (AuthenticationManagerBuilder delegateBuilder : delegateBuilders) {
						delegate = delegateBuilder.getDefaultUserDetailsService();
						if (delegate != null) {
							break;
						}
					}

					if (delegate == null) {
						throw new IllegalStateException("UserDetailsService is required.");
					}
					this.delegateBuilders = null;
				}
			}

			return delegate.loadUserByUsername(username);
		}
	}

	/**
	 * Delays the use of the AuthenticationManager build from the
	 * AuthenticationManagerBuilder to ensure that it has been fully configured.
     * 内部嵌套类,该类的目的是包装一个 AuthenticationManager , 该被包装的
     * AuthenticationManager 会由该 AuthenticationManagerDelegator 的构造函数
     * 参数对象 delegateBuilder 在目标 AuthenticationManager 首次被使用时构建。
     * 这么做的目的是确保 AuthenticationManager 被调用时,它已经被完全配置。
	 *
	 * @author Rob Winch
	 * @since 3.2
	 */
	static final class AuthenticationManagerDelegator implements AuthenticationManager {
		private AuthenticationManagerBuilder delegateBuilder;
		private AuthenticationManager delegate;
		private final Object delegateMonitor = new Object();
		private Set<String> beanNames;

		AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder,
				ApplicationContext context) {
			Assert.notNull(delegateBuilder, "delegateBuilder cannot be null");
			Field parentAuthMgrField = ReflectionUtils.findField(
					AuthenticationManagerBuilder.class, "parentAuthenticationManager");
			ReflectionUtils.makeAccessible(parentAuthMgrField);
			beanNames = getAuthenticationManagerBeanNames(context);
			validateBeanCycle(
					ReflectionUtils.getField(parentAuthMgrField, delegateBuilder),
					beanNames);
			this.delegateBuilder = delegateBuilder;
		}

		public Authentication authenticate(Authentication authentication)
				throws AuthenticationException {
			if (delegate != null) {
            // 如果被代理的 AuthenticationManager delegate 已经被构建则直接使用它进行认证
				return delegate.authenticate(authentication);
			}

			synchronized (delegateMonitor) {
				if (delegate == null) {
					// 如果被代理的 AuthenticationManager delegate 尚未被构建,则在本次认证调用
					// 中先对其进行构建,构建成功后忘掉所用的delegateBuilder
					// 该模式中,这次认证也是对被代理的目标 AuthenticationManager 的首次认证调用
					delegate = this.delegateBuilder.getObject();
					this.delegateBuilder = null;
				}
			}

			// 对目标 AuthenticationManager 的首次认证调用
			return delegate.authenticate(authentication);
		}

		// 从指定应用上下文及其祖先上下文中查找类型为  AuthenticationManager 的 bean 的名称,可能有多个
		private static Set<String> getAuthenticationManagerBeanNames(
				ApplicationContext applicationContext) {
			String[] beanNamesForType = BeanFactoryUtils
					.beanNamesForTypeIncludingAncestors(applicationContext,
							AuthenticationManager.class);
			return new HashSet<>(Arrays.asList(beanNamesForType));
		}

		// 确保没有循环依赖 
		private static void validateBeanCycle(Object auth, Set<String> beanNames) {
			if (auth != null && !beanNames.isEmpty()) {
				if (auth instanceof Advised) {
					Advised advised = (Advised) auth;
					TargetSource targetSource = advised.getTargetSource();
					if (targetSource instanceof LazyInitTargetSource) {
						LazyInitTargetSource lits = (LazyInitTargetSource) targetSource;
						if (beanNames.contains(lits.getTargetBeanName())) {
							throw new FatalBeanException(
						"A dependency cycle was detected when trying to resolve the AuthenticationManager. "
                        + " Please ensure you have configured authentication.");
						}
					}
				}
				beanNames = Collections.emptySet();
			}
		}
	}

	static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {
		private PasswordEncoder defaultPasswordEncoder;

		/**
		 * Creates a new instance
		 *
		 * @param objectPostProcessor the ObjectPostProcessor instance to use.
		 */
		DefaultPasswordEncoderAuthenticationManagerBuilder(
			ObjectPostProcessor<Object> objectPostProcessor, PasswordEncoder defaultPasswordEncoder) {
			super(objectPostProcessor);
			this.defaultPasswordEncoder = defaultPasswordEncoder;
		}

		@Override
		public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
			throws Exception {
			return super.inMemoryAuthentication()
				.passwordEncoder(this.defaultPasswordEncoder);
		}

		@Override
		public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication()
			throws Exception {
			return super.jdbcAuthentication()
				.passwordEncoder(this.defaultPasswordEncoder);
		}

		@Override
		public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> 
				userDetailsService(T userDetailsService) throws Exception {
			return super.userDetailsService(userDetailsService)
				.passwordEncoder(this.defaultPasswordEncoder);
		}
	}

    // 内部嵌套类,延迟口令/密码加密器,将对口令/密码加密器对象的获取延迟到对其进行首次调用时
	static class LazyPasswordEncoder implements PasswordEncoder {
		private ApplicationContext applicationContext;
		private PasswordEncoder passwordEncoder;

		LazyPasswordEncoder(ApplicationContext applicationContext) {
			this.applicationContext = applicationContext;
		}

		@Override
		public String encode(CharSequence rawPassword) {
			return getPasswordEncoder().encode(rawPassword);
		}

		@Override
		public boolean matches(CharSequence rawPassword,
			String encodedPassword) {
			return getPasswordEncoder().matches(rawPassword, encodedPassword);
		}

		@Override
		public boolean upgradeEncoding(String encodedPassword) {
			return getPasswordEncoder().upgradeEncoding(encodedPassword);
		}

		private PasswordEncoder getPasswordEncoder() {
			if (this.passwordEncoder != null) {
				return this.passwordEncoder;
			}
			PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
			if (passwordEncoder == null) {
				passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
			}
			this.passwordEncoder = passwordEncoder;
			return passwordEncoder;
		}

		private <T> T getBeanOrNull(Class<T> type) {
			try {
				return this.applicationContext.getBean(type);
			} catch(NoSuchBeanDefinitionException notFound) {
				return null;
			}
		}

		@Override
		public String toString() {
			return getPasswordEncoder().toString();
		}
	}
}

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值