// 这里取得用户输入的用户名
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
// 如果配置了缓存,从缓存中去取以前存入的用户验证信息 - 这里是UserDetail,是服务器端存在数据库里的用户信息,这样就不用每次都去数据库中取了
boolean cacheWasUsed = true;
UserDetails user = this.userCache.getUserFromCache(username);
//没有取到,设置标志位,下面会把这次取到的服务器端用户信息存入缓存中去
if (user == null) {
cacheWasUsed = false;
try {//这里是调用UserDetailService去取用户数据库里信息的地方
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
} catch (UsernameNotFoundException notFound) {
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");
}
if (!user.isAccountNonLocked()) {
throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked",
"User account is locked"));
}
if (!user.isEnabled()) {
throw new DisabledException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled",
"User is disabled"));
}
if (!user.isAccountNonExpired()) {
throw new AccountExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired",
"User account has expired"));
}
// This check must come here, as we don't want to tell users
// about account status unless they presented the correct credentials
try {//这里是验证过程,在retrieveUser中从数据库中得到用户的信息,在additionalAuthenticationChecks中进行对比用户输入和服务器端的用户信息
//如果验证通过,那么构造一个Authentication对象来让以后的授权使用,如果验证不通过,直接抛出异常结束鉴权过程
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
} catch (AuthenticationException exception) {
if (cacheWasUsed) {
// There was a problem, so try again after checking
// we're using latest data (ie not from the cache)
cacheWasUsed = false;
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
} else {
throw exception;
}
}
if (!user.isCredentialsNonExpired()) {
throw new CredentialsExpiredException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.credentialsExpired", "User credentials have expired"));
}
//根据前面的缓存结果决定是不是要把当前的用户信息存入缓存以供下次验证使用
if (!cacheWasUsed) {
this.userCache.putUserInCache(user);
}
Object principalToReturn = user;
if (forcePrincipalAsString) {
principalToReturn = user.getUsername();
}
//最后返回Authentication记录了验证结果供以后的授权使用
return createSuccessAuthentication(principalToReturn, authentication, user);
}
//这是是调用UserDetailService去加载服务器端用户信息的地方,从什么地方加载要看设置,这里我们假设由JdbcDaoImp来从数据中进行加载
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedUser;
//这里调用UserDetailService去从数据库中加载用户验证信息,同时返回从数据库中返回的信息,这些信息放到了UserDetails对象中去了
try {
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
} catch (DataAccessException repositoryProblem) {
throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
if (loadedUser == null) {
throw new AuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
。
下面我们重点分析一下JdbcDaoImp这个类来看看具体是怎样从数据库中得到用户信息的:
Java代码 ![]()
- publicclass JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {
- //~ Static fields/initializers =====================================================================================
- //这里是预定义好的对查询语句,对应于默认的数据库表结构,也可以自己定义查询语句对应特定的用户数据库验证表的设计
- public static final String DEF_USERS_BY_USERNAME_QUERY =
- "SELECT username,password,enabled FROM users WHERE username = ?";
- public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
- "SELECT username,authority FROM authorities WHERE username = ?";
- //~ Instance fields ================================================================================================
- //这里使用Spring JDBC来进行数据库操作
- protected MappingSqlQuery authoritiesByUsernameMapping;
- protected MappingSqlQuery usersByUsernameMapping;
- private String authoritiesByUsernameQuery;
- private String rolePrefix = "";
- private String usersByUsernameQuery;
- private boolean usernameBasedPrimaryKey = true;
- //~ Constructors ===================================================================================================
- //在初始化函数中把查询语句设置为预定义的SQL语句
- public JdbcDaoImpl() {
- usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY;
- authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY;
- }
- //~ Methods ========================================================================================================
- protected void addCustomAuthorities(String username, List authorities) {}
- public String getAuthoritiesByUsernameQuery() {
- return authoritiesByUsernameQuery;
- }
- public String getRolePrefix() {
- return rolePrefix;
- }
- public String getUsersByUsernameQuery() {
- return usersByUsernameQuery;
- }
- protected void initDao() throws ApplicationContextException {
- initMappingSqlQueries();
- }
- * Extension point to allow other MappingSqlQuery objects to be substituted in a subclass
- */
- protected void initMappingSqlQueries() {
- this.usersByUsernameMapping = new UsersByUsernameMapping(getDataSource());
- this.authoritiesByUsernameMapping = new AuthoritiesByUsernameMapping(getDataSource());
- }
- 作
本文详细解析了Spring Security中的用户认证流程,包括从用户输入获取用户名、利用缓存提高效率、验证用户状态(如锁定、过期等)、密码校验及最终认证结果的返回。

1465

被折叠的 条评论
为什么被折叠?



