天天看點

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

------------------

auto-config Automatically registers a login form, BASIC authentication, logout services. If set to "true", all of these capabilities are added (although you can still customize the configuration of each by providing the respective element).

可以了解為:

下面是Spring Security Filter Chain的清單:

Alias

Filter Class

Namespace Element or Attribute

CHANNEL_FILTER

<code>ChannelProcessingFilter</code>

<code>http/intercept-url@requires-channel</code>

SECURITY_CONTEXT_FILTER

<code>SecurityContextPersistenceFilter</code>

<code>http</code>

CONCURRENT_SESSION_FILTER

<code>ConcurrentSessionFilter</code>

<code>session-management/concurrency-control</code>

HEADERS_FILTER

<code>HeaderWriterFilter</code>

<code>http/headers</code>

CSRF_FILTER

<code>CsrfFilter</code>

<code>http/csrf</code>

LOGOUT_FILTER

<code>LogoutFilter</code>

<code>http/logout</code>

X509_FILTER

<code>X509AuthenticationFilter</code>

<code>http/x509</code>

PRE_AUTH_FILTER

<code>AstractPreAuthenticatedProcessingFilter</code> Subclasses

N/A

CAS_FILTER

<code>CasAuthenticationFilter</code>

FORM_LOGIN_FILTER

<code>UsernamePasswordAuthenticationFilter</code>

<code>http/form-login</code>

BASIC_AUTH_FILTER

<code>BasicAuthenticationFilter</code>

<code>http/http-basic</code>

SERVLET_API_SUPPORT_FILTER

<code>SecurityContextHolderAwareRequestFilter</code>

<code>http/@servlet-api-provision</code>

JAAS_API_SUPPORT_FILTER

<code>JaasApiIntegrationFilter</code>

<code>http/@jaas-api-provision</code>

REMEMBER_ME_FILTER

<code>RememberMeAuthenticationFilter</code>

<code>http/remember-me</code>

ANONYMOUS_FILTER

<code>AnonymousAuthenticationFilter</code>

<code>http/anonymous</code>

SESSION_MANAGEMENT_FILTER

<code>SessionManagementFilter</code>

<code>session-management</code>

EXCEPTION_TRANSLATION_FILTER

<code>ExceptionTranslationFilter</code>

FILTER_SECURITY_INTERCEPTOR

<code>FilterSecurityInterceptor</code>

SWITCH_USER_FILTER

<code>SwitchUserFilter</code>

其中紅色标出的二個Filter對應的是 “登出、登入”,如果不使用auto-config=true,開發人員可以自行“重寫”這二個Filter來達到類似的目的,比如:預設情況下,登入表單必須使用post方式送出,在一些安全性相對不那麼高的場景中(比如:企業内網應用),如果希望通過類似 http://xxx/login?username=abc&amp;password=123的方式直接登入,可以參考下面的代碼:

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

View Code

即:從UsernamePasswordAuthenticationFilter繼承一個類,然後把關于POST方式判斷的代碼注釋掉即可。預設情況下,Spring Security的使用者名是區分大小寫,如果覺得沒必要,上面的代碼同時還示範了如何在Filter中自動将其轉換成大寫。

預設情況下,登入成功後,Spring Security有自己的handler處理類,如果想在登入成功後,加一點自己的處理邏輯,可參考下面的代碼:

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

類似的,要自定義LogoutFilter,可參考下面的代碼:

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

即:從LogoutFilter繼承一個類,如果還想在退出後加點自己的邏輯(比如登出後,清空額外的Cookie之類\記錄退出時間、地點之類),可重寫doFilter方法,但不建議這樣,有更好的做法,自行定義logoutSuccessHandler,然後在運作時,通過構造函數注入即可。

下面是自定義退出成功處理的handler示例:

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

這二個Filter弄好後,剩下的就是改配置:

使用者輸入“使用者名、密碼”,并點選完登入後,最終實作校驗的是AuthenticationProvider,而且一個webApp中可以同時使用多個Provider,下面是一個自定義Provider的示例代碼:

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

這裡僅僅隻是出于示範目的,人為留了一個後門,隻要使用者名在白名單之列,不管輸入什麼密碼,都可以通過!(再次提示:隻是出于示範目的,千萬不要在實際項目中使用)

相關的配置節點修改如下:

運作時,Spring Security将會按照順序,依次從上向下調用所有Provider,隻要任何一個Provider校驗通過,整個認證将通過。這也意味着:使用者yjmyzz/123456以及白名單中的使用者名均可以登入系統。這是一件很有意思的事情,試想一下,如果有二個現成的系統,各有自己的使用者名/密碼(包括不同的存儲機制),想把他們內建在一個登入頁面使用,技術上講,隻要實作二個Provider各自對應不同的處理,可以很輕易的實作多個系統的認證內建。(注:當然實際應用中,多個系統的認證內建,更多的是采用SSO來處理,這裡隻是提供了另一種思路)

最後來看下如何自定義AuthenticationToken,如果我們想在登入頁上加一些額外的輸入項(比如:驗證碼,安全問題之類),

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

為了能讓這些額外添加的輸入項,傳遞到Provider中參與驗證,就需要對UsernamePasswordAuthenticationToken進行擴充,參考代碼如下:

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

這裡擴充了二個屬性:questionId、answer,為了友善後面“詩句問題"的回答進行判斷,還得先做點其它準備工作

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

預定義了幾句唐詩,key即為questionId,value為 "題目/答案"格式。此外,如果答錯了,為了友善向使用者提示錯誤原因,還要定義一個異常類:(注:Spring Security中,所有驗證失敗,都是通過直接抛異常來處理的)

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

原來的CustomLoginFilter也要相應的修改,以接收額外添加的二個參數:

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

現在,CustomAuthenticationProvider中的additionalAuthenticationChecks方法中,就能拿到使用者送出的下一句答案,進行相關驗證了:

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

最後來處理前端的login頁面及Action

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

代碼很簡單,從預定義的詩句中,随機挑一句,并把questionId及question放到model中,傳給view

Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken
Spring Security筆記:自定義Login/Logout Filter、AuthenticationProvider、AuthenticationToken

ok,完工!

不過,有一個小問題要提醒一下:對本文所示案例而言,因為同時應用了二個Provider,一個是預設的,一個是我們後來自定義的,而對"下一句"的答案驗證,隻在CustomAuthenticationProvider中做了處理,換句話說,如果使用者在界面上輸入的使用者名/密碼是yjmyzz/123456,根據前面講到的規則,預設的Provider會先起作用,認證通過直接忽略”下一句“的驗證,隻有輸入白名單中的使用者名時,才會走CustomAuthenticationProvider的驗證流程。