SpringSecurity认证专题之【AuthenticationManager】

时间:2022-07-28
本文章向大家介绍SpringSecurity认证专题之【AuthenticationManager】,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

  哈喽,大家好,最近有段时间没有写博客了,今天开始我会陆续给大家整理出SpringSecurity原理源码相关的文件,本篇文章主要是给大家介绍下认证体系中最基础的AuthenticationManager的内容,让你对它从整体上面有一个认知。

AuthenticationManager

  首先我们来看下AuthenticationManager这个接口的定义。

public interface AuthenticationManager {
	/**
	* 定义的一个认证的方法
	**/
	Authentication authenticate(Authentication authentication)
			throws AuthenticationException;
}

  通过源码能发现,单纯的就是定义了一个认证的方法,所以要分析的话我们要看下他的实现,在SpringSecurity中默认的AuthenticationManager的实现是ProviderManager.

ProviderManager

ProviderManagerAuthenticationManager的默认实现,但是ProviderManager并没有提供具体的认证逻辑,而是具有多个AuthenticationProvider. 也就是在ProviderManager中支持多种认证方式,而AuthenticationProvider就是一种具体的认证。

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
		InitializingBean {
	// 多种认证方式
	private List<AuthenticationProvider> providers = Collections.emptyList();

	private AuthenticationManager parent;
	private boolean eraseCredentialsAfterAuthentication = true;

	/**
	 * 遍历每一种认证方式,进行认证
	 **/
	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;
			}
			// 忽略...
			try {
				// 调用具体的认证方法
				result = provider.authenticate(authentication);

				if (result != null) {
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException | InternalAuthenticationServiceException e) {
				prepareException(e, authentication);
				throw e;
			} catch (AuthenticationException e) {
				lastException = e;
			}
		}
		// 忽略...
	}
}

  通过上图可以简单的体现这三者之间的关系

AuthenticationProvider

  现在我们来看下AuthenticationProvider的具体认证流程的实现

public interface AuthenticationProvider {
	/**
	 * 认证逻辑实现的方法
	 */
	Authentication authenticate(Authentication authentication)
			throws AuthenticationException;

	/**
	 * 判断当前provider是否支持该Authentication
	 * /
	boolean supports(Class<?> authentication);
}

  源码中的方法相比AuthenticationManager中多了一个supports方法,主要是用来做支持判断的。具体是认证实现还要看其具体的实现,默认的实现是DaoAuthenticationProvider,还有个中间抽象类AbstractUserDetailsAuthenticationProvider

AbstractUserDetailsAuthenticationProvider

  在其中定义了认证的主要流程

public abstract class AbstractUserDetailsAuthenticationProvider implements
		AuthenticationProvider, InitializingBean, MessageSourceAware {
	// 抽象定义
	protected abstract void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException;

	// 定义主要流程,关键账号验证和密码验证在具体实现类中实现
	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 {
				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();
		}

		return createSuccessAuthentication(principalToReturn, authentication, user);
	}

	
		UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
				principal, authentication.getCredentials(),
				authoritiesMapper.mapAuthorities(user.getAuthorities()));
		result.setDetails(authentication.getDetails());

		return result;
	}
	// 抽象的 账号验证
	protected abstract UserDetails retrieveUser(String username,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException;

	public boolean supports(Class<?> authentication) {
		return (UsernamePasswordAuthenticationToken.class
				.isAssignableFrom(authentication));
	}

}

DaoAuthenticationProvider

  默认的具体认证实现

	protected final UserDetails retrieveUser(String username,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		prepareTimingAttackProtection();
		try {
			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);
		}
	}
	@SuppressWarnings("deprecation")
	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();

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

  上图是整理的相关的整体的结构,通过上图应该能够对于相关的结构关系会有一个整体的认证,下篇文章我们会给大家梳理下初始化的过程