阅读完需:约 5 分钟
在 UsernamePasswordAuthenticationFilter 源码分析 中,最后在类UsernamePasswordAuthenticationFilter 的验证方法 attemptAuthentication() 会将用户表单提交过来的用户名和密码封装成对象委托类 AuthenticationManager 的验证方法 authenticate() 进行身份验证。
那么本文主要对 AuthenticationManager 的验证方法 authenticate() 验证原理进行源码分析。
AuthenticationManager 相关类图
AuthenticationManager 验证过程涉及到的类和接口较多,先用一张类图说明各个类和接口之间的关系,如下:

-
AuthenticationManager为认证管理接口类,其定义了认证方法authenticate()。 -
ProviderManager为认证管理类,实现了接口AuthenticationManager,并在认证方法authenticate()中将身份认证委托给具有认证资格的AuthenticationProvider进行身份认证。ProviderManager中的成员变量providers [List<AuthenticationProvider>]存储了一个AuthenticationProvider类型的List,和 spring security 配置文件相对应,如下图:

-
AuthenticationProvider为认证接口类,其定义了身份认证方法authenticate()。
-
AbstractUserDetailsAuthenticationProvider为认证抽象类,实现了接口AuthenticationProvider定义的认证方法authenticate()。AbstractUserDetailsAuthenticationProvider还定义了虚拟方法retrieveUser()用作查询数据库用户信息,以及虚拟方法additionalAuthenticationChecks()用作身份认证。
-
DaoAuthenticationProvider继承自类AbstractUserDetailsAuthenticationProvider,实现该类的方法retrieveUser()和additionalAuthenticationChecks()。DaoAuthenticationProvider中还具有成员变量userDetailsService [UserDetailsService]用作用户信息查询,以及成员变量passwordEncoder [PasswordEncoder]用作密码的加密及验证。DaoAuthenticationProvider和 spring security 配置文件相对应,如下图所示:

流程分析
1、认证的入口为 AuthenticationManager 的 authenticate()方法,鉴于 AuthenticationManager是接口类,因此分析它的实现类 ProviderManager,ProviderManager 的 authenticate() 方法代码如下:
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
Authentication result = null;
boolean debug = logger.isDebugEnabled();
-->1 for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
if (debug) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
}
try {
-->2 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.
try {
result = 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 = e;
}
}
if (result != null) {
if (eraseCredentialsAfterAuthentication
&& (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data
// from authentication
((CredentialsContainer) result).eraseCredentials();
}
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}"));
}
prepareException(lastException, authentication);
throw lastException;
}
其中:
-
-->1处的for循环从该类的属性providers[List<AuthenticationProvider>]中去取到支持该认证的AuthenticationProvider来进行认证处理。 -
-->2处代码为使用支持该认证的AuthenticationProvider对用户身份进行认证,使用该类进行认证如下文所示。
2、在上文代码的 -->2 处调用的代码 result = provider.authenticate(authentication);,使用了 AuthenticationProvider 的 authenticate() 方法进行认证,接下来分析该方法,鉴于 AuthenticationProvider 是一个接口,因此分析它的实现类 AbstractUserDetailsAuthenticationProvider 的子类 DaoAuthenticationProvider 的认证方法 authenticate(),代码如下所示:
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 {
-->1 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);
-->2 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);
}
其中:
-
-->1处的代码表示调用方法retrieveUser()从数据库中加载用户信息。该方法代码如下:
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedUser;
try {
-->1.1 loadedUser = this.getUserDetailsService().loadUserByUsername(username);
}
catch (UsernameNotFoundException notFound) {
if (authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials().toString();
passwordEncoder.isPasswordValid(userNotFoundEncodedPassword,
presentedPassword, null);
}
throw notFound;
}
catch (Exception repositoryProblem) {
throw new InternalAuthenticationServiceException(
repositoryProblem.getMessage(), repositoryProblem);
}
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
在上述代码中 -->1.1 处代码的意思为:调用成员变量 userDetailsService 的方法 loadUserByUsername() 加载数据层中的用户信息(是不是很熟悉)。
-
-->2处的代码为调用方法additionalAuthenticationChecks()密码验证,该方法代码如下:
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
Object salt = null;
if (this.saltSource != null) {
salt = this.saltSource.getSalt(userDetails);
}
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();
->2.1 if (!passwordEncoder.isPasswordValid(userDetails.getPassword(),
presentedPassword, salt)) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
->2.1 出代码调用了成员变量 passwordEncoder 的校验方法 isPasswordValid() 对用户密码进行验证。(是不是很熟悉)

