1.问题描述:
在认证失败后UsernameNotFoundException异常会被默认转换成BadCredentialsException异常,导致不能捕获到UsernameNotFoundException, 一般来说UsernameNotFoundException是用户名错误导致的登录失败,BadCredentialsException是用户名或者密码错误导致的登录失败。如果我们能捕获到UsernameNotFoundException就能很好的区分登录失败是哪种具体的原因,以便在某些时候做一些对应的逻辑处理。
2.解决方案:
在AbstractUserDetailsAuthenticationProvider中有这么一段代码:
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
//...
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");
}
// ...
}
可以看到hideUserNotFoundExceptions为true的时候UsernameNotFoundException就会被转换成BadCredentialsException异常,而hideUserNotFoundExceptions的默认值是true,所以我们只需要修改hideUserNotFoundExceptions为false即可解决问题。以下是具体方案:
在WebSecurityConfigurerAdapter的实现类配置中添加如下配置:
@Primary
@Order(90)
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
//...
@Override
public void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
//可以配置成一个Bean
private DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
//设置hideUserNotFoundExceptions为false
provider.setHideUserNotFoundExceptions(false);
//userDetailsService是你自己的userDetailsService实现类,
//UsernameNotFoundException就是其中抛出来的
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
//...
}
认证失败事件处理:
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
@Component
public class AuthenticationFailureEvenHandler implements ApplicationListener<AbstractAuthenticationFailureEvent> {
@Override
public void onApplicationEvent(AbstractAuthenticationFailureEvent event) {
AuthenticationException authenticationException = event.getException();
Authentication authentication = (Authentication) event.getSource();
handle(authenticationException, authentication);
}
public void handle(AuthenticationException exc, Authentication auth){
//获取一些参数
String username = (String)auth.getPrincipal();
Map<String,Object> params= (Map<String,Object>)auth.getDetails();
if (exc instanceof BadCredentialsException){
//密码错误导致的登录失败
//TODO...
}else if(exc instanceof UsernameNotFoundException){
//用户名不存在导致的登录失败
//TODO...
}
}
}