Spring--攔截器,字元編碼過濾器
1. SpringMVC中的Interceptor
1.1. 練習目標
在主頁顯示的“使用者名稱”位置新增超連結,點選後進入“個人中心”。
通過/user/info.do
路徑,可以顯示“個人資訊”頁面,該頁面需要登入後才允許訪問。
1.2. 分析問題
一個專案中,可能絕大部分的功能都是需要登入後才允許使用的,但是,在這些功能的處理中,可能都需要執行:
// 判斷Session中是否有username if (session.getAttribute("username") == null) { // 沒有,則意味著沒有登入,則重定向到登入頁 return "redirect:login.do"; }
而大量的複製並貼上以上程式碼的做法顯然是不可取的!
1.3. 解決方案
攔截器(Interceptor)是SpringMVC中的元件,它是執行在DispatcherServlet
之後、每個Controller
之前的元件,並且,執行時,可以選擇攔截或放行,則會導致某些請求可以被處理或不被處理!然後,攔截器還可以執行在每個Controller
處理完請求之後。
基於攔截器這樣的特點,可以在專案中新增“登入攔截器”,使得要求登入的請求都先經過“登入攔截器”進行判斷,在“登入攔截器”中將判斷Session是否有效,如果有效,則放行,如果沒有有效的Session,則直接攔截,並重定向到登入頁。
1.4. 使用方式
1.4.1. 建立攔截器
所有的自定義攔截器,都必須實現HandlerInterceptor
介面,所以,在專案中建立cn.huang.spring.interceptor.LoginInterceptor
,實現HandlerInterceptor
:
public class LoginInterceptor implements HandlerInterceptor { public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("LoginInterceptor.preHandle()"); return false; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("LoginInterceptor.postHandle()"); } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("LoginInterceptor.afterCompletion()"); } }
1.4.2. 配置攔截器
在springmvc.xml
中新增攔截器的配置:
<!-- 配置攔截器鏈 -->
<mvc:interceptors>
<!-- 配置第1個攔截器 -->
<mvc:interceptor>
<!-- 1. 攔截的路徑 -->
<mvc:mapping path="/user/info.do" />
<mvc:mapping path="/main/index.do" />
<!-- 2. 指定攔截器類 -->
<bean class="cn.huang.spring.interceptor.LoginInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
1.4.3. 執行效果
當嘗試訪問以上攔截的路徑時,可以看到攔截器的preHandle()
方法被執行,且介面上顯示一片空白,而沒有被攔截器路徑可以正常訪問,攔截器中的任何方法都沒有執行。
1.4.4. 完善登入攔截
在控制器中,成功登入時,會在Session中放入使用者名稱,且存入時使用的名稱是username
,所以,判斷是否登入,就是檢查Session是否有名為username
的值,如果值是存在的,即已經登入,如果值不存在,則沒有登入,所以,在攔截器的preHandle()
方法中:
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler)
throws Exception {
System.out.println("LoginInterceptor.preHandle()");
// 獲取Session
HttpSession session
= request.getSession();
// 判斷session中是否有登入資訊
if (session.getAttribute("username") == null) {
// 沒有登入資訊,則:重定向到登入頁
response.sendRedirect("../user/login.do");
// 執行攔截
return false;
} else {
// 有登入資訊,則:允許正常訪問,直接放行
return true;
}
}
1.4.5. 攔截器的更多配置
在配置攔截器時,使用<mvc:mapping path="" />
可以配置攔截哪些路徑,同一個攔截器的配置中,可以有多個這樣的節點。
在配置攔截路徑時,可以使用*
作為萬用字元,例如:
<mvc:mapping path="/user/*" />
以上配置將表示攔截/user
之下的所有路徑,例如:/user/login.do
、/user/reg.do
、/user/handler_login.do
、/user/handler_reg.do
、/user/inf.do
等。
使用1個星號(*
)只能通配1層路徑,即以上配置對於/user/info/change.do
這樣的路徑是不起作用的!如果需要無視路徑的層級,應該使用2個星號(*
),即配置為:
<mvc:mapping path="/user/**" />
使用以上配置,可適用於例如:/user/login.do
、/user/info/change.do
、/user/info/change/password.do
等。
由於使用於萬用字元後,攔截的範圍可能過大,還可以使用<mvc:exclude-mapping />
節點來配置例外:
<mvc:interceptor>
<!-- 1. 攔截的路徑 -->
<mvc:mapping path="/user/**" />
<mvc:mapping path="/main/index.do" />
<!-- 2. 例外的路徑,不攔截的路徑,即白名單-->
<mvc:exclude-mapping path="/user/reg.do" />
<mvc:exclude-mapping path="/user/handle_reg.do" />
<mvc:exclude-mapping path="/user/login.do" />
<mvc:exclude-mapping path="/user/handle_reg.do" />
<!-- 3. 指定攔截器類 -->
<bean class="cn.huang.spring.interceptor.LoginInterceptor" />
</mvc:interceptor>
這些例外可以通俗的理解為白名單,即攔截器對於這些路徑的請求完全不受理。
注意:以上配置,必須先配置攔截路徑,再配置例外,最後配置攔截器類。
2. 字元編碼過濾器
使用SpringMVC框架時,預設並不支援中文,為了能夠支援中文,需要通過request.setCharacterEncoding("utf-8")
這類的語法來指定使用的字元編碼!
這項任務不能夠在控制器(Controller
)中來完成,因為,指定編碼必須在Servlet
或其之前就執行,如果執行到Servlet
時就已經亂碼,然後,在控制器中再來調整,就已經晚了!也就意味著,通過SpringMVC的攔截器是無法實現編碼的調整的!
解決方案是使用SpringMVC框架中自帶的CharacterEncodingFilter
類,這個類是一個過濾器類,是執行在所有的Servlet
之前的,所以,通過過濾器指定了編碼後,適用於所有的Servlet
及後續的流程!
這個類並沒有明顯的指定使用的編碼,所以,在配置時,還需要通過初始化引數來確定所使用的編碼!
具體的配置是在web.xml
中新增:
<!-- 配置字元編碼過濾器 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>