天天看點

解決errorpage裡面取不到Authentication的問題

            本人原創,發現一些網站無道德的抓取,請自覺删去内容,轉載請注明出處:

       http://asialee.iteye.com/blog/1772860

      項目中遇到一個很奇怪的問題,在錯誤頁面404裡面取不到目前登入使用者,即 SecurityContextHolder.getContext().getAuthentication()取不到目前的登陸使用者資訊。這個問題花了我很長時間最終搞定了,下面講一下解決問題的過程。

          首先來看一下項目的異常處理方式,在web.xml裡面配置了錯誤頁:

<error-page>
		<error-code>404</error-code>
		<location>/WEB-INF/pages/errors/404.jsp</location>
	</error-page>      

           當通路一個不存在的url時,spring的前端控制器的邏輯如下:

解決errorpage裡面取不到Authentication的問題

        其實就是會調用noHandlerFound函數,然後直接退出DispatcherServlet。

        我們再來一下noHandlerFound的邏輯:

解決errorpage裡面取不到Authentication的問題

         在這個裡面實際上是傳回一個404的錯誤,真正的錯誤頁面處理的轉向是由tomcat容器來完成的。通過調試發現在這個地方SecurityContextHolder.getContext().getAuthentication()還有值,但是通路404頁面的tag裡面就取不到了,後來通過監控網絡發現,通路errorpage是由容器重新發起的一個請求,這個請求裡面拿不到Authentication可能是沒有走springsecurity的前端攔截器 springSecurityFilterChain。

         我們來看一下springSecurityFilterChain 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>      

         突然想到極有可能是這個filter沒有轉發,後來看了下filter-mapping的配置,還真是這樣。filter-mapping裡面接受dispatcher參數。

         我們就來看一下這個參數的含義。

          2.4版本的servlet規範在部屬描述符中新增加了一個<dispatcher>元素,這個元素有四個可能的值:即REQUEST,FORWARD,INCLUDE和ERROR,可以在一個<filter-mapping>元素中加入任意數目的<dispatcher>,使得filter将會作用于直接從用戶端過來的request,通過forward過來的request,通過include過來的request和通過<error-page>過來的request。如果沒有指定任何< dispatcher >元素,預設值是REQUEST。

            可以通過下面幾個例子來輔助了解。 

<filter-mapping> 
<filter-name>Logging Filter</filter-name> 
<url-pattern>/products/*</url-pattern> 
</filter-mapping>      

           這種情況下,過濾器将會作用于直接從用戶端發過來的以/products/…開始的請求。因為這裡沒有制定任何的< dispatcher >元素,預設值是REQUEST。

<filter-mapping> 
<filter-name>Logging Filter</filter-name> 
<servlet-name>ProductServlet</servlet-name> 
<dispatcher>INCLUDE</dispatcher> 
</filter-mapping>       

        這種情況下,如果請求是通過request dispatcher的include方法傳遞過來的對ProductServlet的請求,則要經過這個過濾器的過濾。其它的諸如從用戶端直接過來的對ProductServlet的請求等都不需要經過這個過濾器。 

        指定filter的比對方式有兩種方法:直接指定url-pattern和指定servlet,後者相當于把指定的servlet對應的url-pattern作為filter的比對模式 

filter的路徑比對和servlet是一樣的,都遵循servlet規範中《SRV.11.2 Specification of Mappings》一節的說明 

<filter-mapping> 
<filter-name>Logging Filter</filter-name> 
<url-pattern>/products/*</url-pattern> 
<dispatcher>FORWARD</dispatcher> 
<dispatcher>REQUEST</dispatcher> 
</filter-mapping>       

         看了這個,我修改了下springSecurityFilterChain的filter-mapping的配置,就     好了。

          修改後的配置如下:

<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>
		<dispatcher>REQUEST</dispatcher>
		<dispatcher>ERROR</dispatcher>
	</filter-mapping>      

         意思就是直接從用戶端過來的request和通過<error-page>過來的request 都要走這個filter,配置完後就果斷好了。

         好了,就寫到這裡了,希望對大家有所幫助。關于springSecurityFilterChain這個我會另寫一篇部落格進行詳細講解。