经过查看源码可得知:前端
抽象类中AbstractUserDetailsAuthenticationProvider 接口抛出异常AuthenticationExceptionajax
下面源码注释这么描述spring
* * @throws AuthenticationException if the credentials could not be validated * (generally a <code>BadCredentialsException</code>, an * <code>AuthenticationServiceException</code> or * <code>UsernameNotFoundException</code>) */ protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException;
AuthenticationException 抛出的状况是 BadCredentialsException,AuthenticationServiceException,UsernameNotFoundException这三个异常。数据库
当UserNameNotFoundException 这个异常的状况下会抛出
可实际状况下咱们 查询的user为null 抛出了 UserNameNotFoundException 这个异常可是实际并无抛出来,抛出的是 AuthenticationException session
经过继续往下查看源码后明白了,原来是作了对UserNameNotFoundException 处理,转换成了AuthenticationException 这个异常;ide
hideUserNotFoundExceptions = true; ... 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; } }
因此咱们没有抛出UsernameNotFoundException 这个异常,而是将这个异常进行了转换。post
如何抛出这个异常,那就是将hideUserNotFoundExceptions 设置为 false;this
@Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setHideUserNotFoundExceptions(false); provider.setUserDetailsService(mUserDetailsService); provider.setPasswordEncoder(passwordEncoder); return provider; }
最后在WebSecurityConfig配置便可加密
auth.authenticationProvider(daoAuthenticationProvider());
设置以前
设置以后spa
抛出的UsernameNotFoundException 异常已经捕获到了,而后进入if中
最后抛出
new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"))
会将异常信息存入session中 , 根据key便可获取
最后在失败的处理器中获取到
@Component public class MyAuthenctiationFailureHandler extends SimpleUrlAuthenticationFailureHandler { public MyAuthenctiationFailureHandler() { this.setDefaultFailureUrl("/loginPage"); } @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { logger.info("进入认证失败处理类"); HttpSession session = request.getSession(); AuthenticationException attributes = (AuthenticationException) session.getAttribute("SPRING_SECURITY_LAST_EXCEPTION"); logger.info("aaaa " + attributes); super.onAuthenticationFailure(request, response, exception); } }
这样子作能够直接在session中获取到,若是自定义抛出一个异常首先控制台会报异常错,其次前台的经过如ajax获取错误信息,又得写ajax。
这样子作直接将信息存入session中,springSecurity直接为咱们封装到session中了,能够直接根据key获取到。
注意
若是用户名不存在,抛了异常
不要再在密码验证其中抛出密码错误异常,否则抛出UserNameNotFoundException 后还会验证密码是否正确,若是密码正确还好,返回true,若是不正确抛出异常。
此时会将 UsernameNotFoundException 异常覆盖,这里应该返回false。
源码以下:
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); } }
mitigateAgainstTimingAttack 方法
private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) { if (authentication.getCredentials() != null) { String presentedPassword = authentication.getCredentials().toString(); //这里会是自定义密码加密 this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword); } }
个人密码加密器
@Override public boolean matches(CharSequence charSequence, String s) { String pwd = charSequence.toString(); log.info("前端传过来密码为: " + pwd); log.info("加密后密码为: " + MD5Util.encode(charSequence.toString())); log.info("数据库存储的密码: " + s); //s 应在数据库中加密 if( MD5Util.encode(charSequence.toString()).equals(MD5Util.encode(s))){ return true; } //throw new DisabledException("--密码错误--"); //不能抛出异常 return false; }
以下是 咱们密码验证器里抛出异常后获取到的异常
异常
密码未验证 以前捕获到的异常信息
验证密码后捕获到的异常 (这里跳到ProviderManager中)
既然我用户名不对我就没必要要验证密码了,因此不该该抛出异常,应该直接返回false。
否则。此处密码异常会将 用户不存在进行覆盖!
<body> 登陆页面 <div th:text="${session.SPRING_SECURITY_LAST_EXCEPTION!=null?session.SPRING_SECURITY_LAST_EXCEPTION.message:''}">[...]</div> <form method="post" action="/login" > <input type="text" name="username" /><br> <input type="password" name="password" /> <input type="submit" value="login" /> </form>