AbstractUserDetailsAuthenticationProvider类位于org.springframework.security.authentication.dao包下,在
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter中被调用,
该类的public Authentication authenticate(Authentication authentication) throws AuthenticationException方法百常重要,通过这段代码能详细了解整个验证的过程,下面对该方法的代码分段进行说明:
(1) Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
"Only UsernamePasswordAuthenticationToken is supported"));
//要求传入的authentication对象必须是UsernamePasswordAuthenticationToken类或其子类的实例
(2)
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
//从authentication中取出登录名
(3) boolean cacheWasUsed = true;
UserDetails user = this.userCache.getUserFromCache(username);
//默认情况下从缓存中(UserCache接口实现)取出用户信息
(4)
if (user == null) {
//如果从内存中取不到用户,则设置cacheWasUsed 为false,供后面使用
cacheWasUsed = false;
try {
//retrieveUser是抽象方法,通过子类来实现获取用户的信息,以UserDetails接口形式返回
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");
}
(5)
try {
//preAuthenticationChecks和additionalAuthenticationChecks这是UserDetailsChecker接口的实现类
//验证帐号是否锁定\是否禁用\帐号是否到期
preAuthenticationChecks.check(user);
//由子类来完成更进一步的验证
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
} catch (AuthenticationException exception) {
//下面这段代码体现了老外的细腻之处,意思是说如果在调用某个UserDetailsChecker接口的实现类验证失败后,就判断下用户信息是否从内存中得到,如果之前是从内存中得到的用户信息,那么考虑到可能数据是不实时的,就重新通过retrieveUser方法去取出用户信息,再次重复进行检查验证
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;
}
}
(6) //在spring的框架设计中经常能看到这样的前置处理和后置处理,此处后置处理只是判断了下用户的密码是否过期,如过期则记入日志
postAuthenticationChecks.check(user);
//如果没有缓存则进行缓存,则处的userCache是由NullUserCache类实现的,名如其义,该类的putUserInCache没做任何事
if (!cacheWasUsed) {
this.userCache.putUserInCache(user);
}
(7)
//以下代码主要是把用户的信息和之前用户提交的认证信息重新组合成一个authentication实例返回,返回类是
UsernamePasswordAuthenticationToken类的实例
Object principalToReturn = user;
if (forcePrincipalAsString) {
principalToReturn = user.getUsername();
}
return createSuccessAuthentication(principalToReturn, authentication, user);
-----------------------------------------------------------------------------------
代码分析到此,另外再附上类图方便理解。