這個教程是我在往項目中一點一點添加?Spring Security的過程的一個筆記,也是我學習?Spring Security的一個過程。
在解決這個問題之前要先說一點authentication-provider默認加載的是DaoAuthenticationProvider類。
完成了上一章的內容后在測試的時候發現在UserDetailsService中拋出的UsernameNotFoundException無法被捕獲。于是找到DaoAuthenticationProvider,源碼看了好幾遍沒有看出端倪。然后直接查看最頂級的接口AuthenticationProvider。發現它只有一個方法如下
~~~
Authentication authenticate(Authentication authentication) throws AuthenticationException;
~~~
拋出AuthenticationException異常,而UsernameNotFoundException是AuthenticationException的子類,那問題應該就出在authenticate這個方法上了。
然后找到DaoAuthenticationProvider的父類AbstractUserDetailsAuthenticationProvider的authenticate方法,發現了這段代碼。
~~~
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;
}
}
~~~
它這里有個hideUserNotFoundExceptions屬性,默認是true。這樣的話即便我們拋出了UsernameNotFoundException它也會轉為BadCredentialsException,所以我們需要將hideUserNotFoundExceptions屬性的值設為false,而在上一章中的那種配置方法是沒有辦法為其屬性賦值的所以我們要手動注入.authentication-provider,所以配置就變成了下面的內容
~~~
<sec:authentication-manager>
<sec:authentication-provider ref="authenticationProvider" />
</sec:authentication-manager>
<bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="hideUserNotFoundExceptions" value="false" />
<property name="userDetailsService" ref="userDetailService" />
<property name="userCache" ref="userCache" />
<property name="messageSource" ref="messageSource" />
<property name="passwordEncoder" ref="passwordEncode" />
<property name="saltSource" ref="saltSource" />
</bean>
<!-- 配置密碼加密類 -->
<bean id="passwordEncode" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
<bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<property name="userPropertyToUse" value="username"/>
</bean>
~~~
注意:如果在authentication-provider配置中用ref指定AuthenticationProvider則authentication-provider的子元素將都不可以用。
即下面的這種配置是錯誤的
~~~
<sec:authentication-manager>
<sec:authentication-provider ref="authenticationProvider" >
<sec:password-encoder ref="passwordEncode">
<sec:salt-source user-property="username"/>
</sec:password-encoder>
</sec:authentication-provider>
</sec:authentication-manager>
~~~
所以我們的鹽值加密就需要注入到AuthenticationProvider中了。
SaltSource是一個接口有兩個實現類SystemWideSaltSource和ReflectionSaltSource。
SystemWideSaltSource?:只能指定固定值
ReflectionSaltSource:可以指定UserDetails的屬性,這里我們用的就是它
這樣的話就可以保證在拋出UsernameNotFoundException時,前臺能顯示出來錯誤信息,如下所示。

在上一章中忘了介紹如何在前臺顯示登錄是的異常信息,在這里補上。
UsernamePasswordAuthenticationFilter認證失敗后,異常信息會寫到Session中,key為SPRING_SECURITY_LAST_EXCEPTION
可以通過El表達式來獲取到異常的信息。
~~~
${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}
~~~
- 前言
- (大綱)----學習過程分享
- (1)----SpringSecurity3.2環境搭建
- (2)----SpringSecurity簡單測試
- (3)---- 自定義登錄頁面
- (4)---- 數據庫表結構的創建
- (5)---- 國際化配置及UserCache
- (6)---- 使用數據庫管理用戶及權限
- (7)---- 解決UsernameNotFoundException無法被捕獲的問題
- (8)---- 自定義決策管理器及修改權限前綴
- (9)---- 自定義AccessDeniedHandler
- (10)---- 自定義登錄成功后的處理程序及修改默認驗證地址
- (11)---- 使用數據庫來管理資源
- (12)---- 使用數據庫來管理方法
- (13)---- 驗證碼功能的實現
- (14)---- Logout和SessionManager