• SpringSecurity - 启动流程分析(六)



    活动地址:CSDN21天学习挑战赛

    前言

    在上篇文章 SpringSecurity - 启动流程分析(五) 中,我们知道具体的认证逻辑是交给了 AuthenticationManager 来处理的,查看实现类可以看到只有一个 ProviderManager 实现类(其他都是内部类):

    public class ProviderManager implements AuthenticationManager, MessageSourceAware,
    		InitializingBean {
    
    	private static final Log logger = LogFactory.getLog(ProviderManager.class);
    
    	private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
    	private List<AuthenticationProvider> providers = Collections.emptyList();
    	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    	private AuthenticationManager parent;
    	private boolean eraseCredentialsAfterAuthentication = true;
    
    	public ProviderManager(AuthenticationProvider... providers) {
    		this(Arrays.asList(providers), null);
    	}
    
    	public ProviderManager(List<AuthenticationProvider> providers) {
    		this(providers, null);
    	}
    
    	public ProviderManager(List<AuthenticationProvider> providers,
    			AuthenticationManager parent) {
    		Assert.notNull(providers, "providers list cannot be null");
    		this.providers = providers;
    		this.parent = parent;
    		checkState();
    	}
    	
    	public void afterPropertiesSet() {
    		checkState();
    	}
    
    	private void checkState() {
    		if (parent == null && providers.isEmpty()) {
    			throw new IllegalArgumentException(
    					"A parent AuthenticationManager or a list "
    							+ "of AuthenticationProviders is required");
    		} else if (providers.contains(null)) {
    			throw new IllegalArgumentException(
    					"providers list cannot contain null values");
    		}
    	}
    
    	// 核心认证方法
    	public Authentication authenticate(Authentication authentication)
    			throws AuthenticationException {
    		...
    	}
    
    	@SuppressWarnings("deprecation")
    	private void prepareException(AuthenticationException ex, Authentication auth) {
    		eventPublisher.publishAuthenticationFailure(ex, auth);
    	}
    
    	private void copyDetails(Authentication source, Authentication dest) {
    		if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
    			AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;
    
    			token.setDetails(source.getDetails());
    		}
    	}
    
    	public List<AuthenticationProvider> getProviders() {
    		return providers;
    	}
    
    	public void setMessageSource(MessageSource messageSource) {
    		this.messages = new MessageSourceAccessor(messageSource);
    	}
    
    	public void setAuthenticationEventPublisher(
    			AuthenticationEventPublisher eventPublisher) {
    		Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");
    		this.eventPublisher = eventPublisher;
    	}
    
    	public void setEraseCredentialsAfterAuthentication(boolean eraseSecretData) {
    		this.eraseCredentialsAfterAuthentication = eraseSecretData;
    	}
    
    	public boolean isEraseCredentialsAfterAuthentication() {
    		return eraseCredentialsAfterAuthentication;
    	}
    
    	private static final class NullEventPublisher implements AuthenticationEventPublisher {
    		public void publishAuthenticationFailure(AuthenticationException exception,
    				Authentication authentication) {
    		}
    
    		public void publishAuthenticationSuccess(Authentication authentication) {
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    我们大致看一下 ProviderManager 源码,可以看到其中一个变量:

    private List<AuthenticationProvider> providers = Collections.emptyList();
    
    • 1

    这个变量存储了需要认证的 provider

    接着查看一下核心方法:authenticate()

    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();
    
    	for (AuthenticationProvider provider : getProviders()) {
    		if (!provider.supports(toTest)) {
    			continue;
    		}
    
    		if (debug) {
    			logger.debug("Authentication attempt using "
    					+ provider.getClass().getName());
    		}
    
    		try {
    			result = provider.authenticate(authentication);
    
    			if (result != null) {
    				copyDetails(authentication, result);
    				break;
    			}
    		}
    		catch (AccountStatusException | InternalAuthenticationServiceException e) {
    			prepareException(e, authentication);
    			// SEC-546: Avoid polling additional providers if auth failure is due to
    			// invalid account status
    			throw e;
    		} catch (AuthenticationException e) {
    			lastException = e;
    		}
    	}
    
    	if (result == null && parent != null) {
    		// Allow the parent to try.
    		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 then 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 then 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    可以看到,authenticate() 方法中循环调用了 List 中的 authenticate() 方法。

    到这里为止我们知道了认证是交给了各种 AuthenticationProvider 的组合来处理的。查看 AuthenticationProvider 的实现类:

    在这里插入图片描述

    分析

    接下来我们分析一下 SpringSecurity 是什么时候初始化 AuthenticationManagerProviderManager

    SpringSecurity - 启动流程分析(二) 这篇文章中,我们知道默认的过滤器链是通过调用 WebSecurityConfigurerAdaptergetHttp() 方法获取到 HttpSecurity,只有由 HttpSecurity 生成过滤器链。我们来查看以下 getHttp() 方法:

    protected final HttpSecurity getHttp() throws Exception {
    	...
    	// 核心方法,初始化 AuthenticationManager
    	AuthenticationManager authenticationManager = authenticationManager();
    	authenticationBuilder.parentAuthenticationManager(authenticationManager);
    	Map<Class<?>, Object> sharedObjects = createSharedObjects();
    
    	http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
    			sharedObjects);
    	...
    	return http;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    查看 authenticationManager() 方法:

    protected AuthenticationManager authenticationManager() throws Exception {
    	if (!authenticationManagerInitialized) {
    		configure(localConfigureAuthenticationBldr);
    		if (disableLocalConfigureAuthenticationBldr) {
    			// 核心流程
    			authenticationManager = authenticationConfiguration
    					.getAuthenticationManager();
    		}
    		else {
    			authenticationManager = localConfigureAuthenticationBldr.build();
    		}
    		authenticationManagerInitialized = true;
    	}
    	return authenticationManager;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    查看 AuthenticationConfigurationgetAuthenticationManager() 方法:

    // 添加 GlobalAuthenticationConfigurerAdapter 类型的 Bean 到 IoC容器 中
    @Bean
    public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
    		ApplicationContext context) {
    	return new EnableGlobalAuthenticationAutowiredConfigurer(context);
    }
    // 添加 GlobalAuthenticationConfigurerAdapter 类型的 Bean 到 IoC容器 中
    @Bean
    public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
    	return new InitializeUserDetailsBeanManagerConfigurer(context);
    }
    // 添加 GlobalAuthenticationConfigurerAdapter 类型的 Bean 到 IoC容器 中
    @Bean
    public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(ApplicationContext context) {
    	return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
    }
    	
    @Autowired(required = false)
    public void setGlobalAuthenticationConfigurers(
    		List<GlobalAuthenticationConfigurerAdapter> configurers) {
    	configurers.sort(AnnotationAwareOrderComparator.INSTANCE);
    	this.globalAuthConfigurers = configurers;
    }
    
    @Bean
    public AuthenticationManagerBuilder authenticationManagerBuilder(
    		ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
    	// 初始化默认的 PasswordEncoder,跟踪源码可知是 DelegatingPasswordEncoder
    	LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
    	AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);
    
    	// 设置 AuthenticationManagerBuilder
    	DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
    	if (authenticationEventPublisher != null) {
    		result.authenticationEventPublisher(authenticationEventPublisher);
    	}
    	return result;
    }
    
    public AuthenticationManager getAuthenticationManager() throws Exception {
    	if (this.authenticationManagerInitialized) {
    		return this.authenticationManager;
    	}
    	// 这里添加了默认的内部类 DefaultPasswordEncoderAuthenticationManagerBuilder 到 IoC容器 中
    	AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
    	if (this.buildingAuthenticationManager.getAndSet(true)) {
    		return new AuthenticationManagerDelegator(authBuilder);
    	}
    	// 在 setGlobalAuthenticationConfigurers 中自动注入并排序
    	for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {
    		// 具体的逻辑,跟踪源码可以看到里面初始化了 DaoAuthenticationProvider
    		authBuilder.apply(config);
    	}
    
    	// 初始化完成 AuthenticationManager
    	authenticationManager = authBuilder.build();
    
    	if (authenticationManager == null) {
    		authenticationManager = getAuthenticationManagerBean();
    	}
    
    	this.authenticationManagerInitialized = true;
    	return authenticationManager;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    通过以上源码跟踪,我们知道了默认会初始化一个 DaoAuthenticationProvider 作为 ProviderManager 中的 List 属性的值。

  • 相关阅读:
    LineageOS:Android开源手机操作系统的未来之路
    三款Zookeeper可视化工具、ZooInspector、prettyZoo、ZooKeeperAssistant
    《算法竞赛进阶指南》 最大子序和
    多线程之间如何进行通信 ?
    握住音乐的法宝 - 简谱
    C语言程序的编译(预处理)概述 —— 上
    Mathorcup数学建模竞赛第四届-【妈妈杯】C题:家庭暑假旅游套餐的设计(附MATLAB和SAS代码)
    什么是MapReduce?MapReduce整体架构搭建使用介绍
    尚医通_第11章_医院排班管理和搭建用户系统环境
    产品管理系统(MVC设计模式)——第一个Javaee项目
  • 原文地址:https://blog.csdn.net/qiaohao0206/article/details/126338492