spring security中的AuthenticationManager

发布时间 2023-04-17 21:47:58作者: 程序晓猿

一、AuthenticationManager

AuthenticationManager是spring security中的认证管理器用来对登录请求进行处理。举个例子讲,在使用表单登录时处理用户的登录请求的是UsernamePasswordAuthenticationFilter 这个过滤器,它内部持有一个AuthenticationManager的对象,处理认证登录请求时会调用AuthenticationManager#authenticate方法处理请求。

1.1 AuthenticationManager

AuthenticationManager是一个接口,是对认证管理器进行的抽象,先来看下它的源码

public interface AuthenticationManager {
    //调用authenticate对认证请求进行处理。
    //入参是一个Authentication对象,封装了用户名和密码
    //返回一个已经被认证过的Authentication对象
	Authentication authenticate(Authentication authentication)
			throws AuthenticationException;
}

1.2 Authentication

接着看下Authentication这个类,它用来对用户信息进行封装

public interface Authentication extends Principal, Serializable {

    //这个方法返回用户的权限信息
	Collection<? extends GrantedAuthority> getAuthorities();

	//返回用户的凭证信息,例如密码
	Object getCredentials();

	/** 返回用户的一些额外信息,例如ip等,可以自定义多个属性
	 * Stores additional details about the authentication request. These might be an IP
	 * address, certificate serial number etc.
	 *
	 * @return additional details about the authentication request, or <code>null</code>
	 * if not used
	 */
	Object getDetails();

	/** 返回用户主体,使用表单登录时返回的是用户的用户名
	 */
	Object getPrincipal();

	//用来判断当前这个Authentication对象是否认证通过
	boolean isAuthenticated();

	//设置当前这个Authentication对象的认证状态
	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

接着看下UsernamePasswordAuthenticationFilter这个类中是如何使用认证管理器的

1.3 UsernamePasswordAuthenticationFilter

看一个过滤器的源码我们都会从doFilter方法看起,

UsernamePasswordAuthenticationFilter,这个类继承了AbstractAuthenticationProcessingFilter,doFilter方法定义在它的父类中,

AbstractAuthenticationProcessingFilter部分源码

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
implements ApplicationEventPublisherAware, MessageSourceAware {
	//该过滤器中持有的认证管理器对象
	private AuthenticationManager authenticationManager;
	//session管理器策略
	private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
	//登录成功和登录失败时的处理器
	private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
	private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
	
	//处理登录请求的方法
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {

		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;

		if (!requiresAuthentication(request, response)) {
			chain.doFilter(request, response);

			return;
		}

		if (logger.isDebugEnabled()) {
			logger.debug("Request is to process authentication");
		}

		Authentication authResult;

		try {
			//处理登录请求的方法,这个方法在本类中是抽象方法,会被子类
			//UsernamePasswordAuthenticationFilter实现
			authResult = attemptAuthentication(request, response);
			if (authResult == null) {
				//说明UsernamePasswordAuthenticationFilter这个过滤器并没有完成认证但也没报错
				// return immediately as subclass has indicated that it hasn't completed
				// authentication
				return;
			}
			sessionStrategy.onAuthentication(authResult, request, response);
		}
		catch (InternalAuthenticationServiceException failed) {
			logger.error(
					"An internal error occurred while trying to authenticate the user.",
					failed);
			//认证过程中报错了,属于认证失败。
			unsuccessfulAuthentication(request, response, failed);

			return;
		}
		catch (AuthenticationException failed) {
			// Authentication failed
			//认证过程中报错了,属于认证失败。
			unsuccessfulAuthentication(request, response, failed);

			return;
		}

		// Authentication success
		if (continueChainBeforeSuccessfulAuthentication) {
		    //认证成功,继续执行过滤器链中其他的过滤器
			chain.doFilter(request, response);
		}
		//认证成功的后续处理
		successfulAuthentication(request, response, chain, authResult);
	}
}

所以认证的关键就在子类的attemptAuthentication方法中

public class UsernamePasswordAuthenticationFilter extends
    AbstractAuthenticationProcessingFilter {
	public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
	public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
	//这两个属性定义了从请求中获取用户名/密码时使用的参数名
	private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
	private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    
    public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {
		if (postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException(
					"Authentication method not supported: " + request.getMethod());
		}
		//从请求对象中获取用户名和密码
		String username = obtainUsername(request);
		String password = obtainPassword(request);

		if (username == null) {
			username = "";
		}

		if (password == null) {
			password = "";
		}

		username = username.trim();
		//把用户名和密码封装成一个Authentication对象
		UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);

		// Allow subclasses to set the "details" property
        //在此过滤器中是空方法
		setDetails(request, authRequest);
		//先获取认证管理器,然后执行authenticate方法对authRequest这个Authentication对象进行认证
		return this.getAuthenticationManager().authenticate(authRequest);
	}
}

接下来就可以去看下认证管理器的认证逻辑

1.4 ProviderManager

AuthenticationManager是一个接口,springsecurity提供了一个实现类ProviderManager,来看下它的源码

ProviderManager部分源码:

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
    //重要属性,存储该认证管理器中持有的AuthenticationProvider
    //认证时会循环调用持有的provider对请求进行处理,所以真正对请求进行认证的是各种provider
    private List<AuthenticationProvider> providers = Collections.emptyList();
    //当前认证管理器的父管理器,通常被称作全局认证管理器,
    //如果循环完自己的providers后还不能对请求进行认证就会调用parent对请求进行处理
    private AuthenticationManager parent;
    
    //这个方法时具体的认证逻辑
    public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		AuthenticationException parentException = null;
		Authentication result = null;
		Authentication parentResult = null;
		boolean debug = logger.isDebugEnabled();
		//遍历该认证管理器中持有的providers
		for (AuthenticationProvider provider : getProviders()) {
			//看provider是否支持当前请求,如果不支持就跳过进行下个循环
            if (!provider.supports(toTest)) {
				continue;
			}

			if (debug) {
				logger.debug("Authentication attempt using "
						+ provider.getClass().getName());
			}

			try {
                //此provider支持当前请求,调用provider.authenticate
				result = provider.authenticate(authentication);

				if (result != null) {
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException e) {
				prepareException(e, authentication);
				// SEC-546: Avoid polling additional providers if auth failure is due to
				// invalid account status
				throw e;
			}
			catch (InternalAuthenticationServiceException e) {
				prepareException(e, authentication);
				throw e;
			}
			catch (AuthenticationException e) {
				lastException = e;
			}
		}

		if (result == null && parent != null) {
			// Allow the parent to try.
            //如果自己的providers没有处理请求,再调用parent进行一次尝试
			try {
				result = parentResult = parent.authenticate(authentication);
			}
			catch (ProviderNotFoundException e) {
				// ignore as we will throw below if no other exception occurred prior to
				// calling parent and the parent
				// may throw ProviderNotFound even though a provider in the child already
				// handled the request
			}
			catch (AuthenticationException e) {
				lastException = parentException = e;
			}
		}

		if (result != null) {
            //进到这里表示认证成功了
			if (eraseCredentialsAfterAuthentication
					&& (result instanceof CredentialsContainer)) {
				// Authentication is complete. Remove credentials and other secret data
				// from authentication
				((CredentialsContainer) result).eraseCredentials();
			}

			// If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent
			// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
			if (parentResult == null) {
				eventPublisher.publishAuthenticationSuccess(result);
			}
			return result;
		}

		// Parent was null, or didn't authenticate (or throw an exception).

		if (lastException == null) {
			lastException = new ProviderNotFoundException(messages.getMessage(
					"ProviderManager.providerNotFound",
					new Object[] { toTest.getName() },
					"No AuthenticationProvider found for {0}"));
		}

		// If the parent AuthenticationManager was attempted and failed than it will publish an AbstractAuthenticationFailureEvent
		// This check prevents a duplicate AbstractAuthenticationFailureEvent if the parent AuthenticationManager already published it
		if (parentException == null) {
			prepareException(lastException, authentication);
		}

		throw lastException;
	}
}

所以需要接着看下AuthenticationProvider

1.5 AuthenticationProvider

AuthenticationProvider也是一个接口

public interface AuthenticationProvider {


	//具体的认证方法,只有支持时才会被调用
	Authentication authenticate(Authentication authentication)
			throws AuthenticationException;

	//验证此provider是否支持当前请求
	boolean supports(Class<?> authentication);
}

当我们使用表单登录时,使用的provider是DaoAuthenticationProvider

1.6 DaoAuthenticationProvider

DaoAuthenticationProvider#authenticate方法定义在它的父类AbstractUserDetailsAuthenticationProvider中,先看下这个方法

public abstract class AbstractUserDetailsAuthenticationProvider implements
    AuthenticationProvider, InitializingBean, MessageSourceAware {
    
    //具体的认证方法
    public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
				() -> messages.getMessage(
						"AbstractUserDetailsAuthenticationProvider.onlySupports",
						"Only UsernamePasswordAuthenticationToken is supported"));

		// Determine username
		String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
				: authentication.getName();

		boolean cacheWasUsed = true;
		UserDetails user = this.userCache.getUserFromCache(username);

		if (user == null) {
			cacheWasUsed = false;

			try {
                  //缓存中获取不到用户时在执行获取用户的方法
                  // 这个方法需要被子类实现,所以具体逻辑在DaoAuthenticationProvider
				user = retrieveUser(username,
						(UsernamePasswordAuthenticationToken) authentication);
			}
			catch (UsernameNotFoundException notFound) {
				logger.debug("User '" + username + "' not found");

				if (hideUserNotFoundExceptions) {
					throw new BadCredentialsException(messages.getMessage(
							"AbstractUserDetailsAuthenticationProvider.badCredentials",
							"Bad credentials"));
				}
				else {
					throw notFound;
				}
			}

			Assert.notNull(user,
					"retrieveUser returned null - a violation of the interface contract");
		}

		try {
			preAuthenticationChecks.check(user);
            //这个方法对用户名和密码进行校验,本来中是抽象方法,需要子类来实现
			additionalAuthenticationChecks(user,
					(UsernamePasswordAuthenticationToken) authentication);
		}
		catch (AuthenticationException exception) {
			if (cacheWasUsed) {
				// There was a problem, so try again after checking
				// we're using latest data (i.e. not from the cache)
				cacheWasUsed = false;
				user = retrieveUser(username,
						(UsernamePasswordAuthenticationToken) authentication);
				preAuthenticationChecks.check(user);
				additionalAuthenticationChecks(user,
						(UsernamePasswordAuthenticationToken) authentication);
			}
			else {
				throw exception;
			}
		}

		postAuthenticationChecks.check(user);

		if (!cacheWasUsed) {
			this.userCache.putUserInCache(user);
		}

		Object principalToReturn = user;

		if (forcePrincipalAsString) {
			principalToReturn = user.getUsername();
		}
		//认证成功创建一个新的Authentication对象返回
		return createSuccessAuthentication(principalToReturn, authentication, user);
	}
    
    	protected Authentication createSuccessAuthentication(Object principal,
			Authentication authentication, UserDetails user) {
		UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
				principal, authentication.getCredentials(),
				authoritiesMapper.mapAuthorities(user.getAuthorities()));
		result.setDetails(authentication.getDetails());

		return result;
	}
}

所以还需要在子类DaoAuthenticationProvider中看下retrieveUser,additionalAuthenticationChecks这两个方法

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    //密码编码器
    private PasswordEncoder passwordEncoder;
    //查询用户信息的service
    private UserDetailsService userDetailsService;
    //查询用户
    protected final UserDetails retrieveUser(String username,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		prepareTimingAttackProtection();
		try {
            //调用自己的UserDetailsService
			UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
			if (loadedUser == null) {
				throw new InternalAuthenticationServiceException(
						"UserDetailsService returned null, which is an interface contract violation");
			}
			return loadedUser;
		}
		catch (UsernameNotFoundException ex) {
			mitigateAgainstTimingAttack(authentication);
			throw ex;
		}
		catch (InternalAuthenticationServiceException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
		}
	}
    
    protected void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		if (authentication.getCredentials() == null) {
			logger.debug("Authentication failed: no credentials provided");

			throw new BadCredentialsException(messages.getMessage(
					"AbstractUserDetailsAuthenticationProvider.badCredentials",
					"Bad credentials"));
		}

		String presentedPassword = authentication.getCredentials().toString();
		//把请求中传过来的密码密文和userDetails中查询出的密文进行匹配
		if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
			logger.debug("Authentication failed: password does not match stored value");

			throw new BadCredentialsException(messages.getMessage(
					"AbstractUserDetailsAuthenticationProvider.badCredentials",
					"Bad credentials"));
		}
	}
    
    @Override
	protected Authentication createSuccessAuthentication(Object principal,
			Authentication authentication, UserDetails user) {
		//这个是更新密码的逻辑,一般不会执行
        boolean upgradeEncoding = this.userDetailsPasswordService != null
				&& this.passwordEncoder.upgradeEncoding(user.getPassword());
		if (upgradeEncoding) {
			String presentedPassword = authentication.getCredentials().toString();
			String newPassword = this.passwordEncoder.encode(presentedPassword);
			user = this.userDetailsPasswordService.updatePassword(user, newPassword);
		}
		return super.createSuccessAuthentication(principal, authentication, user);
	}
    
    //对请求进行判断看是否支持
    public boolean supports(Class<?> authentication) {
		return (UsernamePasswordAuthenticationToken.class
				.isAssignableFrom(authentication));
	}
}

到这里还差查询用户的哪个UserDetailsService没有看

1.7 UserDetailsService

这个接口中定义了查询根据用户名查询用户的方法,会在DaoAuthenticationProvider中使用,需要我们提供他的实现类,通常在系统中会提供一个从数据库中查询用户的实现。

public interface UserDetailsService {
	
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

到这里AuthenticationManager相关的功能就介绍完了,接下里我们研究下在springboot启动过程中这个对象是什么时候创建出来的。

二、AuthenticationManagerBuilder

AuthenticationManagerBuilder是用来创建AuthenticationManager的构建器,它实现了SecurityBuilder,所以

SecurityBuilder 创建对象时的一些方法在它的继承体系中都有。

//部分源码
public class AuthenticationManagerBuilder
		extends
		AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
        implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
        //父级认证管理器
        private AuthenticationManager parentAuthenticationManager;
        //providers集合,这些Providers最后会被添加到构建出的AuthenticationManager中
        private List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
        
        //这是一个用来给构建器中添加provider的方法
        public AuthenticationManagerBuilder authenticationProvider(
			AuthenticationProvider authenticationProvider) {
		this.authenticationProviders.add(authenticationProvider);
		return this;
	    }
	
	//这个方法最终会被调用用来创建认证管理器对象
	@Override
	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进行处理,一般情况不会有这个步骤
		providerManager = postProcess(providerManager);
		return providerManager;
	}
}

在spring security中创建认证管理器时都是使用这个构建器进行创建的,接下来就来研究下什么时候会创建

2.1 builder创建时机 WebSecurityConfigurerAdapter

我们对spring security的使用都是从自定义一个WebSecurityConfigurerAdapter的实现类开始的,更具体的内容可以看下这里spring seurity 自动配置的原理

WebSecurityConfigurerAdapter也是一个SecurityConfigurer,所以首先从这个类的init方法开始

public abstract class WebSecurityConfigurerAdapter implements
    WebSecurityConfigurer<WebSecurity> {
    //三个重要的属性
    //用来生成全局认证管理器的配置类,可能用也可能不用
    private AuthenticationConfiguration authenticationConfiguration;
    //用来生成局部认证管理器的builder
	private AuthenticationManagerBuilder authenticationBuilder;
    //用来生成全局认证管理器的builder,其实就是上边builder生成的认证管理器的parent
	private AuthenticationManagerBuilder localConfigureAuthenticationBldr;
    
    //spring容器启动过程中会执行这个方法给上边的两个builder属性进行赋值
    @Autowired
	public void setApplicationContext(ApplicationContext context) {
		this.context = context;

		ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class);
		LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context);
		//这里创建了两个builder
        //DefaultPasswordEncoderAuthenticationManagerBuilder是当前类的内部类
		authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder);
		localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder) {
			@Override
			public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
				authenticationBuilder.eraseCredentials(eraseCredentials);
				return super.eraseCredentials(eraseCredentials);
			}

		};
	}
    //容器启动过程中会给AuthenticationConfiguration这个属性赋值
    @Autowired
	public void setAuthenticationConfiguration(
			AuthenticationConfiguration authenticationConfiguration) {
		this.authenticationConfiguration = authenticationConfiguration;
	}
    
    //重点的init方法,这个方法的分析可以看下上边连接给出的另一篇博客
    public void init(final WebSecurity web) throws Exception {
		//获取http对象
        final HttpSecurity http = getHttp();
		web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
			public void run() {
				FilterSecurityInterceptor securityInterceptor = http
						.getSharedObject(FilterSecurityInterceptor.class);
				web.securityInterceptor(securityInterceptor);
			}
		});
	}
    
    //getHttp方法
    protected final HttpSecurity getHttp() throws Exception {
		if (http != null) {
			return http;
		}

		DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
				.postProcess(new DefaultAuthenticationEventPublisher());
		localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
		//获取全局的认证管理器,也就是parent,这个方法重点看下
		AuthenticationManager authenticationManager = authenticationManager();
        //设置parent,
        //认证的时候如果自己不能认证就会使用parent再进行一次认证
        //这个builder创建的是局部的认证管理器,它会被放到sharedObjects中,
        //在HttpSecurity的beforeConfigure方法中会拿出这个buildr执行得到一个
        //局部的AuthenticationManager在放进sharedObjects中,这部分内容可以看下我的另一篇
        //分析FormLoginConfigurer的博客
		authenticationBuilder.parentAuthenticationManager(authenticationManager);
		authenticationBuilder.authenticationEventPublisher(eventPublisher);
		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();
			ClassLoader classLoader = this.context.getClassLoader();
			List<AbstractHttpConfigurer> defaultHttpConfigurers =
					SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

			for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
				http.apply(configurer);
			}
		}
		configure(http);
		return http;
	}
    
    //这个方法用来获取全局的认证管理器
    protected AuthenticationManager authenticationManager() throws Exception {
		if (!authenticationManagerInitialized) {
            //这个configure方法可以被子类重写,对localConfigureAuthenticationBldr进行一些定制
            //如果没重写这个方法将不会使用localConfigureAuthenticationBldr来构建
            //AuthenticationManager,使用springsecurity的自动配置
			configure(localConfigureAuthenticationBldr);
			if (disableLocalConfigureAuthenticationBldr) {
                //进到这里表示使用authenticationConfiguration中的自动配置来生成全局认证管理器
                //调用getAuthenticationManager方法
				authenticationManager = authenticationConfiguration
						.getAuthenticationManager();
			}
			else {
                //进到这里表示重写configure方法自定义了localConfigureAuthenticationBldr,
                //就使用它来创建,不使用authenticationConfiguration中的自动配置
				authenticationManager = localConfigureAuthenticationBldr.build();
			}
			authenticationManagerInitialized = true;
		}
		return authenticationManager;
	}
    //上边configure方法本类的实现
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //禁用全局认证管理器的builder,意思是不使用这个builder,使用springsecurity的自动配置
		this.disableLocalConfigureAuthenticationBldr = true;
	}
}

2.2 AuthenticationConfiguration中的自动配置

AuthenticationConfiguration针对AuthenticationManager做了许多自动配置,保证能够生成一个AuthenticationManager对象,我们先来研究下使用自动配置时会如何创建出一个AuthenticationManager对象。

在WebSecurityConfigurerAdapter中是通过调用authenticationConfiguration.getAuthenticationManager方法来生成认证管理器对象。

@Configuration
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {
    // 存储特定SecurityConfigurer的集合,下边会有一个set方法自动给此属性注入值
    private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections
			.emptyList();
    //这个类中给spring容器中注入了几个bean
    
    //注入了一个认证管理器的构建器
    @Bean
	public AuthenticationManagerBuilder authenticationManagerBuilder(
			ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
		LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
		AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);

		DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
		if (authenticationEventPublisher != null) {
			result.authenticationEventPublisher(authenticationEventPublisher);
		}
		return result;
	}
    
    @Bean
	public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
			ApplicationContext context) {
		return new EnableGlobalAuthenticationAutowiredConfigurer(context);
	}

    //给容器中注入了一个SecurityConfigurer,
    //这个configurer会给authenticationManagerBuilder中注入一个DaoAuthenticationProvider,
    //下边会具体分析
	@Bean
	public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
		return new InitializeUserDetailsBeanManagerConfigurer(context);
	}

    //给容器中注入了一个SecurityConfigurer
    //这个configurer会从容器中获取AuthenticationProvider并添加到authenticationManagerBuilder中
	@Bean
	public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
		return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
	}
    
    //这个方法会给当前类自动注入SecurityConfigurer,实际上会把上边用@Bean放入容器的三个
    //SecurityConfigurer注入进来。
    @Autowired(required = false)
	public void setGlobalAuthenticationConfigurers(
			List<GlobalAuthenticationConfigurerAdapter> configurers) throws Exception {
		Collections.sort(configurers, AnnotationAwareOrderComparator.INSTANCE);
		this.globalAuthConfigurers = configurers;
	}
    
    //这个方法就是用来获取AuthenticationManager的
    public AuthenticationManager getAuthenticationManager() throws Exception {
		if (this.authenticationManagerInitialized) {
			return this.authenticationManager;
		}
        //先拿到一个AuthenticationManagerBuilder,调的是本类的方法
		AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder(
				this.objectPostProcessor, this.applicationContext);
		if (this.buildingAuthenticationManager.getAndSet(true)) {
			return new AuthenticationManagerDelegator(authBuilder);
		}
		//把属性globalAuthConfigurers中所有的configurer应用到authBuilder中,
        //这个属性值是spring自动注入进来的,实际就是上边用@Bean放进来的三个configurer
		for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {
			authBuilder.apply(config);
		}
		//执行构建器的build方法
		authenticationManager = authBuilder.build();

		if (authenticationManager == null) {
			authenticationManager = getAuthenticationManagerBean();
		}

		this.authenticationManagerInitialized = true;
		return authenticationManager;
	}
}

接下里需要看下上边用@Bean放进来的几个Configurer的作用

2.3.1 InitializeUserDetailsBeanManagerConfigurer

class InitializeUserDetailsBeanManagerConfigurer
		extends GlobalAuthenticationConfigurerAdapter {

	static final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000;

	private final ApplicationContext context;

	/**
	 * @param context
	 */
	public InitializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
		this.context = context;
	}

	@Override
	public void init(AuthenticationManagerBuilder auth) throws Exception {
		//这个configurer的init方法又给builder中添加了一个内部类
        auth.apply(new InitializeUserDetailsManagerConfigurer());
	}

	class InitializeUserDetailsManagerConfigurer
			extends GlobalAuthenticationConfigurerAdapter {
		@Override
		public void configure(AuthenticationManagerBuilder auth) throws Exception {
			if (auth.isConfigured()) {
				return;
			}
             //从容器中获取UserDetailsService的实现类
			UserDetailsService userDetailsService = getBeanOrNull(
					UserDetailsService.class);
			if (userDetailsService == null) {
				return;
			}
			 //从容器中获取PasswordEncoder的实现类
			PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
			UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
			//创建DaoAuthenticationProvider
			DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
             //给provider中的UserDetailsService赋值
			provider.setUserDetailsService(userDetailsService);
			if (passwordEncoder != null) {
				provider.setPasswordEncoder(passwordEncoder);
			}
			if (passwordManager != null) {
				provider.setUserDetailsPasswordService(passwordManager);
			}
			provider.afterPropertiesSet();
			//把provider添加到构建器中
			auth.authenticationProvider(provider);
		}
}

所以说这个configurer给AuthenticationManagerBuilder中添加了一个DaoAuthenticationProvider。

上边提到了从容器中获取UserDetailsService的实现类,如果有自定义当然会获取到自定义的那个,那如果没有自定义会怎么样呢,需要看下UserDetailsServiceAutoConfiguration

2.3.2 UserDetailsServiceAutoConfiguration

@Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
//条件注解,这个自动配置类只有在使用者没有自定义AuthenticationProvider,UserDetailsService时
//才会生效。
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
		UserDetailsService.class })
public class UserDetailsServiceAutoConfiguration {

	private static final String NOOP_PASSWORD_PREFIX = "{noop}";

	private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern
			.compile("^\\{.+}.*$");

	private static final Log logger = LogFactory
			.getLog(UserDetailsServiceAutoConfiguration.class);

    //这个方法给容器中注入了一个基于内存的UserDetails实现,
	@Bean
	@ConditionalOnMissingBean(type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")
	@Lazy
	public InMemoryUserDetailsManager inMemoryUserDetailsManager(
			SecurityProperties properties,
			ObjectProvider<PasswordEncoder> passwordEncoder) {
		SecurityProperties.User user = properties.getUser();
		List<String> roles = user.getRoles();
		return new InMemoryUserDetailsManager(User.withUsername(user.getName())
				.password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
				.roles(StringUtils.toStringArray(roles)).build());
	}

	private String getOrDeducePassword(SecurityProperties.User user,
			PasswordEncoder encoder) {
		String password = user.getPassword();
		if (user.isPasswordGenerated()) {
			logger.info(String.format("%n%nUsing generated security password: %s%n",
					user.getPassword()));
		}
		if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
			return password;
		}
		return NOOP_PASSWORD_PREFIX + password;
	}

}

2.3.3 InitializeAuthenticationProviderBeanManagerConfigurer

再看下InitializeAuthenticationProviderBeanManagerConfigurer

class InitializeAuthenticationProviderBeanManagerConfigurer
		extends GlobalAuthenticationConfigurerAdapter {

	static final int DEFAULT_ORDER = InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER
			- 100;

	private final ApplicationContext context;

	/**
	 * @param context the ApplicationContext to look up beans.
	 */
	public InitializeAuthenticationProviderBeanManagerConfigurer(
			ApplicationContext context) {
		this.context = context;
	}

	@Override
	public void init(AuthenticationManagerBuilder auth) throws Exception {
		//同样的套路
        auth.apply(new InitializeUserDetailsManagerConfigurer());
	}

	class InitializeUserDetailsManagerConfigurer
			extends GlobalAuthenticationConfigurerAdapter {
		@Override
		public void configure(AuthenticationManagerBuilder auth) throws Exception {
			if (auth.isConfigured()) {
				return;
			}
            //从容器中获取AuthenticationProvider
			AuthenticationProvider authenticationProvider = getBeanOrNull(
					AuthenticationProvider.class);
			if (authenticationProvider == null) {
				return;
			}

			//添加到builder中
			auth.authenticationProvider(authenticationProvider);
		}
        //从这个方法可以看出如果容器中存在多个type类型的bean会返回null
        private <T> T getBeanOrNull(Class<T> type) {
			String[] userDetailsBeanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
					.getBeanNamesForType(type);
			if (userDetailsBeanNames.length != 1) {
				return null;
			}

			return InitializeAuthenticationProviderBeanManagerConfigurer.this.context
					.getBean(userDetailsBeanNames[0], type);
		}
	}
}

2.3 localConfigureAuthenticationBldr的使用

上边第2.2节讲的使用spring security提供的自动配置来生成认证管理器,当不想使用自动配置时就要重写

WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder) 这个方法,2.1节有提到

protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	//重写时要删掉这句,表示启用	LocalConfigureAuthenticationBldr
    this.disableLocalConfigureAuthenticationBldr = true;
    //调用auth提供的方法对它进行定制,一般最常用的是添加自己的AuthenticationProvider,
    //这样后边创建出的认证管理器就会使用我们自己的AuthenticationProvider对请求进行认证
    auth.authenticationProvider(myBuilder)
}