Spring Security Filter

2018-09-28 19:18 更新

Filter

Spring Security 的底層是通過一系列的 Filter 來管理的,每個(gè) Filter 都有其自身的功能,而且各個(gè) Filter 在功能上還有關(guān)聯(lián)關(guān)系,所以它們的順序也是非常重要的。

Filter 順序

Spring Security 已經(jīng)定義了一些 Filter,不管實(shí)際應(yīng)用中你用到了哪些,它們應(yīng)當(dāng)保持如下順序。

  1. ChannelProcessingFilter,如果你訪問的 channel 錯(cuò)了,那首先就會(huì)在 channel 之間進(jìn)行跳轉(zhuǎn),如 http 變?yōu)?https。
  2. SecurityContextPersistenceFilter,這樣的話在一開始進(jìn)行 request 的時(shí)候就可以在 SecurityContextHolder 中建立一個(gè) SecurityContext,然后在請(qǐng)求結(jié)束的時(shí)候,任何對(duì) SecurityContext 的改變都可以被 copy 到 HttpSession。
  3. ConcurrentSessionFilter,因?yàn)樗枰褂?SecurityContextHolder 的功能,而且更新對(duì)應(yīng) session 的最后更新時(shí)間,以及通過 SessionRegistry 獲取當(dāng)前的 SessionInformation 以檢查當(dāng)前的 session 是否已經(jīng)過期,過期則會(huì)調(diào)用 LogoutHandler。
  4. 認(rèn)證處理機(jī)制,如 UsernamePasswordAuthenticationFilter,CasAuthenticationFilter,BasicAuthenticationFilter 等,以至于 SecurityContextHolder 可以被更新為包含一個(gè)有效的 Authentication 請(qǐng)求。
  5. SecurityContextHolderAwareRequestFilter,它將會(huì)把 HttpServletRequest 封裝成一個(gè)繼承自 HttpServletRequestWrapper 的 SecurityContextHolderAwareRequestWrapper,同時(shí)使用 SecurityContext 實(shí)現(xiàn)了 HttpServletRequest 中與安全相關(guān)的方法。
  6. JaasApiIntegrationFilter,如果 SecurityContextHolder 中擁有的 Authentication 是一個(gè) JaasAuthenticationToken,那么該 Filter 將使用包含在 JaasAuthenticationToken 中的 Subject 繼續(xù)執(zhí)行 FilterChain。
  7. RememberMeAuthenticationFilter,如果之前的認(rèn)證處理機(jī)制沒有更新 SecurityContextHolder,并且用戶請(qǐng)求包含了一個(gè) Remember-Me 對(duì)應(yīng)的 cookie,那么一個(gè)對(duì)應(yīng)的 Authentication 將會(huì)設(shè)給 SecurityContextHolder。
  8. AnonymousAuthenticationFilter,如果之前的認(rèn)證機(jī)制都沒有更新 SecurityContextHolder 擁有的 Authentication,那么一個(gè) AnonymousAuthenticationToken 將會(huì)設(shè)給 SecurityContextHolder。
  9. ExceptionTransactionFilter,用于處理在 FilterChain 范圍內(nèi)拋出的 AccessDeniedException 和 AuthenticationException,并把它們轉(zhuǎn)換為對(duì)應(yīng)的 Http 錯(cuò)誤碼返回或者對(duì)應(yīng)的頁(yè)面。
  10. FilterSecurityInterceptor,保護(hù) Web URI,并且在訪問被拒絕時(shí)拋出異常。

添加 Filter 到 FilterChain

當(dāng)我們?cè)谑褂?NameSpace 時(shí),Spring Security 是會(huì)自動(dòng)為我們建立對(duì)應(yīng)的 FilterChain 以及其中的 Filter。但有時(shí)我們可能需要添加我們自己的 Filter 到 FilterChain,又或者是因?yàn)槟承┨匦孕枰约猴@示的定義 Spring Security 已經(jīng)為我們提供好的 Filter,然后再把它們添加到 FilterChain。使用 NameSpace 時(shí)添加 Filter 到 FilterChain 是通過 http 元素下的 custom-filter 元素來定義的。定義 custom-filter 時(shí)需要我們通過 ref 屬性指定其對(duì)應(yīng)關(guān)聯(lián)的是哪個(gè) Filter,此外還需要通過 position、before 或者 after 指定該 Filter 放置的位置。誠(chéng)如在上一節(jié)《Filter 順序》中所提到的那樣,Spring Security 對(duì) FilterChain 中 Filter 順序是有嚴(yán)格的規(guī)定的。Spring Security 對(duì)那些內(nèi)置的 Filter 都指定了一個(gè)別名,同時(shí)指定了它們的位置。我們?cè)诙x custom-filter 的 position、before 和 after 時(shí)使用的值就是對(duì)應(yīng)著這些別名所處的位置。如 position=”CAS_FILTER” 就表示將定義的 Filter 放在 CAS_FILTER 對(duì)應(yīng)的那個(gè)位置,before=”CAS_FILTER” 就表示將定義的 Filter 放在 CAS_FILTER 之前,after=”CAS_FILTER” 就表示將定義的 Filter 放在 CAS_FILTER 之后。此外還有兩個(gè)特殊的位置可以指定,F(xiàn)IRST 和 LAST,分別對(duì)應(yīng)第一個(gè)和最后一個(gè) Filter,如你想把定義好的 Filter 放在最后,則可以使用 after=”LAST”。

接下來我們來看一下 Spring Security 給我們定義好的 FilterChain 中 Filter 對(duì)應(yīng)的位置順序、它們的別名以及將觸發(fā)自動(dòng)添加到 FilterChain 的元素或?qū)傩远x。下面的定義是按順序的。

別名Filter 類對(duì)應(yīng)元素或?qū)傩?/strong>
CHANNEL_FILTERChannelProcessingFilterhttp/intercept-url@requires-channel
SECURITY_CONTEXT_FILTERSecurityContextPersistenceFilterhttp
CONCURRENT_SESSION_FILTERConcurrentSessionFilterhttp/session-management/concurrency-control
LOGOUT_FILTERLogoutFilterhttp/logout
X509_FILTERX509AuthenticationFilterhttp/x509
PRE_AUTH_FILTERAstractPreAuthenticatedProcessingFilter 的子類
CAS_FILTERCasAuthenticationFilter
FORM_LOGIN_FILTERUsernamePasswordAuthenticationFilterhttp/form-login
BASIC_AUTH_FILTERBasicAuthenticationFilterhttp/http-basic
SERVLET_API_SUPPORT_FILTERSecurityContextHolderAwareRequestFilterhttp@servlet-api-provision
JAAS_API_SUPPORT_FILTERJaasApiIntegrationFilterhttp@jaas-api-provision
REMEMBER_ME_FILTERRememberMeAuthenticationFilterhttp/remember-me
ANONYMOUS_FILTERAnonymousAuthenticationFilterhttp/anonymous
SESSION_MANAGEMENT_FILTERSessionManagementFilterhttp/session-management
EXCEPTION_TRANSLATION_FILTERExceptionTranslationFilterhttp
FILTER_SECURITY_INTERCEPTORFilterSecurityInterceptorhttp
SWITCH_USER_FILTERSwitchUserFilter

DelegatingFilterProxy

可能你會(huì)覺得奇怪,我們?cè)?web 應(yīng)用中使用 Spring Security 時(shí)只在 web.xml 文件中定義了如下這樣一個(gè) Filter,為什么你會(huì)說是一系列的 Filter 呢?

   <filter>
      <filter-name>springSecurityFilterChain</filter-name>
     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
   </filter>
   <filter-mapping>
      <filter-name>springSecurityFilterChain</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>

而且如果你不在 web.xml 文件聲明要使用的 Filter,那么 Servlet 容器將不會(huì)發(fā)現(xiàn)它們,它們又怎么發(fā)生作用呢?這就是上述配置中 DelegatingFilterProxy 的作用了。

DelegatingFilterProxy 是 Spring 中定義的一個(gè) Filter 實(shí)現(xiàn)類,其作用是代理真正的 Filter 實(shí)現(xiàn)類,也就是說在調(diào)用 DelegatingFilterProxy 的 doFilter() 方法時(shí)實(shí)際上調(diào)用的是其代理 Filter 的 doFilter() 方法。其代理 Filter 必須是一個(gè) Spring bean 對(duì)象,所以使用 DelegatingFilterProxy 的好處就是其代理 Filter 類可以使用 Spring 的依賴注入機(jī)制方便自由的使用 ApplicationContext 中的 bean。那么 DelegatingFilterProxy 如何知道其所代理的 Filter 是哪個(gè)呢?這是通過其自身的一個(gè)叫 targetBeanName 的屬性來確定的,通過該名稱,DelegatingFilterProxy 可以從 WebApplicationContext 中獲取指定的 bean 作為代理對(duì)象。該屬性可以通過在 web.xml 中定義 DelegatingFilterProxy 時(shí)通過 init-param 來指定,如果未指定的話將默認(rèn)取其在 web.xml 中聲明時(shí)定義的名稱。

   <filter>
      <filter-name>springSecurityFilterChain</filter-name>
     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
   </filter>
   <filter-mapping>
      <filter-name>springSecurityFilterChain</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>

在上述配置中,DelegatingFilterProxy 代理的就是名為 SpringSecurityFilterChain 的 Filter。

需要注意的是被代理的 Filter 的初始化方法 init() 和銷毀方法 destroy() 默認(rèn)是不會(huì)被執(zhí)行的。通過設(shè)置 DelegatingFilterProxy 的 targetFilterLifecycle 屬性為 true,可以使被代理 Filter 與 DelegatingFilterProxy 具有同樣的生命周期。

FilterChainProxy

Spring Security 底層是通過一系列的 Filter 來工作的,每個(gè) Filter 都有其各自的功能,而且各個(gè) Filter 之間還有關(guān)聯(lián)關(guān)系,所以它們的組合順序也是非常重要的。

使用 Spring Security 時(shí),DelegatingFilterProxy 代理的就是一個(gè) FilterChainProxy。一個(gè) FilterChainProxy 中可以包含有多個(gè) FilterChain,但是某個(gè)請(qǐng)求只會(huì)對(duì)應(yīng)一個(gè) FilterChain,而一個(gè) FilterChain 中又可以包含有多個(gè) Filter。當(dāng)我們使用基于 Spring Security 的 NameSpace 進(jìn)行配置時(shí),系統(tǒng)會(huì)自動(dòng)為我們注冊(cè)一個(gè)名為 springSecurityFilterChain 類型為 FilterChainProxy 的 bean(這也是為什么我們?cè)谑褂?SpringSecurity 時(shí)需要在 web.xml 中聲明一個(gè) name 為 springSecurityFilterChain 類型為 DelegatingFilterProxy 的 Filter 了。),而且每一個(gè) http 元素的定義都將擁有自己的 FilterChain,而 FilterChain 中所擁有的 Filter 則會(huì)根據(jù)定義的服務(wù)自動(dòng)增減。所以我們不需要顯示的再定義這些 Filter 對(duì)應(yīng)的 bean 了,除非你想實(shí)現(xiàn)自己的邏輯,又或者你想定義的某個(gè)屬性 NameSpace 沒有提供對(duì)應(yīng)支持等。

Spring security 允許我們?cè)谂渲梦募信渲枚鄠€(gè) http 元素,以針對(duì)不同形式的 URL 使用不同的安全控制。Spring Security 將會(huì)為每一個(gè) http 元素創(chuàng)建對(duì)應(yīng)的 FilterChain,同時(shí)按照它們的聲明順序加入到 FilterChainProxy。所以當(dāng)我們同時(shí)定義多個(gè) http 元素時(shí)要確保將更具有特性的 URL 配置在前。

   <security:http pattern="/login*.jsp*" security="none"/>
   <!-- http 元素的 pattern 屬性指定當(dāng)前的 http 對(duì)應(yīng)的 FilterChain 將匹配哪些 URL,如未指定將匹配所有的請(qǐng)求 -->
   <security:http pattern="/admin/**">
      <security:intercept-url pattern="/**" access="ROLE_ADMIN"/>
   </security:http>
   <security:http>
      <security:intercept-url pattern="/**" access="ROLE_USER"/>
   </security:http>

需要注意的是 http 擁有一個(gè)匹配 URL 的 pattern,未指定時(shí)表示匹配所有的請(qǐng)求,其下的子元素 intercept-url 也有一個(gè)匹配 URL 的 pattern,該 pattern 是在 http 元素對(duì)應(yīng) pattern 基礎(chǔ)上的,也就是說一個(gè)請(qǐng)求必須先滿足 http 對(duì)應(yīng)的 pattern 才有可能滿足其下 intercept-url 對(duì)應(yīng)的 pattern。

Spring Security 定義好的核心 Filter

通過前面的介紹我們知道 Spring Security 是通過 Filter 來工作的,為保證 Spring Security 的順利運(yùn)行,其內(nèi)部實(shí)現(xiàn)了一系列的 Filter。這其中有幾個(gè)是在使用 Spring Security 的 Web 應(yīng)用中必定會(huì)用到的。接下來我們來簡(jiǎn)要的介紹一下 FilterSecurityInterceptor、ExceptionTranslationFilter、SecurityContextPersistenceFilter 和 UsernamePasswordAuthenticationFilter。在我們使用 http 元素時(shí)前三者會(huì)自動(dòng)添加到對(duì)應(yīng)的 FilterChain 中,當(dāng)我們使用了 form-login 元素時(shí) UsernamePasswordAuthenticationFilter 也會(huì)自動(dòng)添加到 FilterChain 中。所以我們?cè)诶?custom-filter 往 FilterChain 中添加自己定義的這些 Filter 時(shí)需要注意它們的位置。

FilterSecurityInterceptor

FilterSecurityInterceptor 是用于保護(hù) Http 資源的,它需要一個(gè) AccessDecisionManager 和一個(gè) AuthenticationManager 的引用。它會(huì)從 SecurityContextHolder 獲取 Authentication,然后通過 SecurityMetadataSource 可以得知當(dāng)前請(qǐng)求是否在請(qǐng)求受保護(hù)的資源。對(duì)于請(qǐng)求那些受保護(hù)的資源,如果 Authentication.isAuthenticated()返回 false 或者 FilterSecurityInterceptor 的 alwaysReauthenticate 屬性為 true,那么將會(huì)使用其引用的 AuthenticationManager 再認(rèn)證一次,認(rèn)證之后再使用認(rèn)證后的 Authentication 替換 SecurityContextHolder 中擁有的那個(gè)。然后就是利用 AccessDecisionManager 進(jìn)行權(quán)限的檢查。

我們?cè)谑褂没?NameSpace 的配置時(shí)所配置的 intercept-url 就會(huì)跟 FilterChain 內(nèi)部的 FilterSecurityInterceptor 綁定。如果要自己定義 FilterSecurityInterceptor 對(duì)應(yīng)的 bean,那么該 bean 定義大致如下所示:

   <bean id="filterSecurityInterceptor"
   class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
      <property name="authenticationManager" ref="authenticationManager" />
      <property name="accessDecisionManager" ref="accessDecisionManager" />
      <property name="securityMetadataSource">
         <security:filter-security-metadata-source>
            <security:intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
            <security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN" />
         </security:filter-security-metadata-source>
      </property>
   </bean>

filter-security-metadata-source 用于配置其 securityMetadataSource 屬性。intercept-url 用于配置需要攔截的 URL 與對(duì)應(yīng)的權(quán)限關(guān)系。

ExceptionTranslationFilter

通過前面的介紹我們知道在 Spring Security 的 Filter 鏈表中 ExceptionTranslationFilter 就放在 FilterSecurityInterceptor 的前面。而 ExceptionTranslationFilter 是捕獲來自 FilterChain 的異常,并對(duì)這些異常做處理。ExceptionTranslationFilter 能夠捕獲來自 FilterChain 所有的異常,但是它只會(huì)處理兩類異常,AuthenticationException 和 AccessDeniedException,其它的異常它會(huì)繼續(xù)拋出。如果捕獲到的是 AuthenticationException,那么將會(huì)使用其對(duì)應(yīng)的 AuthenticationEntryPoint 的 commence()處理。如果捕獲的異常是一個(gè) AccessDeniedException,那么將視當(dāng)前訪問的用戶是否已經(jīng)登錄認(rèn)證做不同的處理,如果未登錄,則會(huì)使用關(guān)聯(lián)的 AuthenticationEntryPoint 的 commence()方法進(jìn)行處理,否則將使用關(guān)聯(lián)的 AccessDeniedHandler 的 handle()方法進(jìn)行處理。

AuthenticationEntryPoint 是在用戶沒有登錄時(shí)用于引導(dǎo)用戶進(jìn)行登錄認(rèn)證的,在實(shí)際應(yīng)用中應(yīng)根據(jù)具體的認(rèn)證機(jī)制選擇對(duì)應(yīng)的 AuthenticationEntryPoint。

AccessDeniedHandler 用于在用戶已經(jīng)登錄了,但是訪問了其自身沒有權(quán)限的資源時(shí)做出對(duì)應(yīng)的處理。ExceptionTranslationFilter 擁有的 AccessDeniedHandler 默認(rèn)是 AccessDeniedHandlerImpl,其會(huì)返回一個(gè) 403 錯(cuò)誤碼到客戶端。我們可以通過顯示的配置 AccessDeniedHandlerImpl,同時(shí)給其指定一個(gè) errorPage 使其可以返回對(duì)應(yīng)的錯(cuò)誤頁(yè)面。當(dāng)然我們也可以實(shí)現(xiàn)自己的 AccessDeniedHandler。

   <bean id="exceptionTranslationFilter"
      class="org.springframework.security.web.access.ExceptionTranslationFilter">
      <property name="authenticationEntryPoint">
         <bean class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
            <property name="loginFormUrl" value="/login.jsp" />
         </bean>
      </property>
      <property name="accessDeniedHandler">
         <bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
            <property name="errorPage" value="/access_denied.jsp" />
         </bean>
      </property>
   </bean>

在上述配置中我們指定了 AccessDeniedHandler 為 AccessDeniedHandlerImpl,同時(shí)為其指定了 errorPage,這樣發(fā)生 AccessDeniedException 后將轉(zhuǎn)到對(duì)應(yīng)的 errorPage 上。指定了 AuthenticationEntryPoint 為使用表單登錄的 LoginUrlAuthenticationEntryPoint。此外,需要注意的是如果該 filter 是作為自定義 filter 加入到由 NameSpace 自動(dòng)建立的 FilterChain 中時(shí)需把它放在內(nèi)置的 ExceptionTranslationFilter 后面,否則異常都將被內(nèi)置的 ExceptionTranslationFilter 所捕獲。

   <security:http>
      <security:form-login login-page="/login.jsp"
         username-parameter="username" password-parameter="password"
         login-processing-url="/login.do" />
      <!-- 退出登錄時(shí)刪除 session 對(duì)應(yīng)的 cookie -->
      <security:logout delete-cookies="JSESSIONID" />
      <!-- 登錄頁(yè)面應(yīng)當(dāng)是不需要認(rèn)證的 -->
      <security:intercept-url pattern="/login*.jsp*"
         access="IS_AUTHENTICATED_ANONYMOUSLY" />
      <security:intercept-url pattern="/**" access="ROLE_USER" />
      <security:custom-filter ref="exceptionTranslationFilter" after="EXCEPTION_TRANSLATION_FILTER"/>
   </security:http>

在捕獲到 AuthenticationException 之后,調(diào)用 AuthenticationEntryPoint 的 commence() 方法引導(dǎo)用戶登錄之前,ExceptionTranslationFilter 還做了一件事,那就是使用 RequestCache 將當(dāng)前 HttpServletRequest 的信息保存起來,以至于用戶成功登錄后需要跳轉(zhuǎn)到之前的頁(yè)面時(shí)可以獲取到這些信息,然后繼續(xù)之前的請(qǐng)求,比如用戶可能在未登錄的情況下發(fā)表評(píng)論,待用戶提交評(píng)論的時(shí)候就會(huì)將包含評(píng)論信息的當(dāng)前請(qǐng)求保存起來,同時(shí)引導(dǎo)用戶進(jìn)行登錄認(rèn)證,待用戶成功登錄后再利用原來的 request 包含的信息繼續(xù)之前的請(qǐng)求,即繼續(xù)提交評(píng)論,所以待用戶登錄成功后我們通??吹降氖怯脩舫晒μ峤涣嗽u(píng)論之后的頁(yè)面。Spring Security 默認(rèn)使用的 RequestCache 是 HttpSessionRequestCache,其會(huì)將 HttpServletRequest 相關(guān)信息封裝為一個(gè) SavedRequest 保存在 HttpSession 中。

SecurityContextPersistenceFilter

SecurityContextPersistenceFilter 會(huì)在請(qǐng)求開始時(shí)從配置好的 SecurityContextRepository 中獲取 SecurityContext,然后把它設(shè)置給 SecurityContextHolder。在請(qǐng)求完成后將 SecurityContextHolder 持有的 SecurityContext 再保存到配置好的 SecurityContextRepository,同時(shí)清除 SecurityContextHolder 所持有的 SecurityContext。在使用 NameSpace 時(shí),Spring Security 默認(rèn)會(huì)給 SecurityContextPersistenceFilter 的 SecurityContextRepository 設(shè)置一個(gè) HttpSessionSecurityContextRepository,其會(huì)將 SecurityContext 保存在 HttpSession 中。此外 HttpSessionSecurityContextRepository 有一個(gè)很重要的屬性 allowSessionCreation,默認(rèn)為 true。這樣需要把 SecurityContext 保存在 session 中時(shí),如果不存在 session,可以自動(dòng)創(chuàng)建一個(gè)。也可以把它設(shè)置為 false,這樣在請(qǐng)求結(jié)束后如果沒有可用的 session 就不會(huì)保存 SecurityContext 到 session 了。SecurityContextRepository 還有一個(gè)空實(shí)現(xiàn),NullSecurityContextRepository,如果在請(qǐng)求完成后不想保存 SecurityContext 也可以使用它。

這里再補(bǔ)充說明一點(diǎn)為什么 SecurityContextPersistenceFilter 在請(qǐng)求完成后需要清除 SecurityContextHolder 的 SecurityContext。SecurityContextHolder 在設(shè)置和保存 SecurityContext 都是使用的靜態(tài)方法,具體操作是由其所持有的 SecurityContextHolderStrategy 完成的。默認(rèn)使用的是基于線程變量的實(shí)現(xiàn),即 SecurityContext 是存放在 ThreadLocal 里面的,這樣各個(gè)獨(dú)立的請(qǐng)求都將擁有自己的 SecurityContext。在請(qǐng)求完成后清除 SecurityContextHolder 中的 SucurityContext 就是清除 ThreadLocal,Servlet 容器一般都有自己的線程池,這可以避免 Servlet 容器下一次分發(fā)線程時(shí)線程中還包含 SecurityContext 變量,從而引起不必要的錯(cuò)誤。

下面是一個(gè) SecurityContextPersistenceFilter 的簡(jiǎn)單配置。

   <bean id="securityContextPersistenceFilter"
   class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
      <property name='securityContextRepository'>
         <bean
         class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
            <property name='allowSessionCreation' value='false' />
         </bean>
      </property>
   </bean>

UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilter 用于處理來自表單提交的認(rèn)證。該表單必須提供對(duì)應(yīng)的用戶名和密碼,對(duì)應(yīng)的參數(shù)名默認(rèn)為 j_username 和 j_password。如果不想使用默認(rèn)的參數(shù)名,可以通過 UsernamePasswordAuthenticationFilter 的 usernameParameter 和 passwordParameter 進(jìn)行指定。表單的提交路徑默認(rèn)是 “j_spring_security_check”,也可以通過 UsernamePasswordAuthenticationFilter 的 filterProcessesUrl 進(jìn)行指定。通過屬性 postOnly 可以指定只允許登錄表單進(jìn)行 post 請(qǐng)求,默認(rèn)是 true。其內(nèi)部還有登錄成功或失敗后進(jìn)行處理的 AuthenticationSuccessHandler 和 AuthenticationFailureHandler,這些都可以根據(jù)需求做相關(guān)改變。此外,它還需要一個(gè) AuthenticationManager 的引用進(jìn)行認(rèn)證,這個(gè)是沒有默認(rèn)配置的。

   <bean id="authenticationFilter"
   class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
      <property name="authenticationManager" ref="authenticationManager" />
      <property name="usernameParameter" value="username"/>
      <property name="passwordParameter" value="password"/>
      <property name="filterProcessesUrl" value="/login.do" />
   </bean>

如果要在 http 元素定義中使用上述 AuthenticationFilter 定義,那么完整的配置應(yīng)該類似于如下這樣子。

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:security="http://www.springframework.org/schema/security"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
          http://www.springframework.org/schema/security
          http://www.springframework.org/schema/security/spring-security-3.1.xsd">
   <!-- entry-point-ref 指定登錄入口 -->
   <security:http entry-point-ref="authEntryPoint">
      <security:logout delete-cookies="JSESSIONID" />
      <security:intercept-url pattern="/login*.jsp*"
         access="IS_AUTHENTICATED_ANONYMOUSLY" />
      <security:intercept-url pattern="/**" access="ROLE_USER" />
      <!-- 添加自己定義的 AuthenticationFilter 到 FilterChain 的 FORM_LOGIN_FILTER 位置 -->
      <security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
   </security:http>
   <!-- AuthenticationEntryPoint,引導(dǎo)用戶進(jìn)行登錄 -->
   <bean id="authEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
      <property name="loginFormUrl" value="/login.jsp"/>
   </bean>
   <!-- 認(rèn)證過濾器 -->
   <bean id="authenticationFilter"
   class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
      <property name="authenticationManager" ref="authenticationManager" />
      <property name="usernameParameter" value="username"/>
      <property name="passwordParameter" value="password"/>
      <property name="filterProcessesUrl" value="/login.do" />
   </bean>

   <security:authentication-manager alias="authenticationManager">
      <security:authentication-provider
         user-service-ref="userDetailsService">
         <security:password-encoder hash="md5"
            base64="true">
            <security:salt-source user-property="username" />
         </security:password-encoder>
      </security:authentication-provider>
   </security:authentication-manager>

   <bean id="userDetailsService"
      class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
      <property name="dataSource" ref="dataSource" />
   </bean>

</beans>
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)