經過查看源碼可得知:前端
抽象類中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>