Spring MVC攔截器(Interceptor)
在系統中,經常需要在處理使用者請求之前和之後執行一些行為,例如檢測使用者的許可權,或者將請求的資訊記錄到日誌中,即平時所說的“許可權檢測”及“日誌記錄”。當然不僅僅這些,所以需要一種機制,攔截使用者的請求,在請求的前後新增處理邏輯。
Spring MVC 提供了 Interceptor 攔截器機制,用於請求的預處理和後處理。
在開發一個網站時可能有這樣的需求:某些頁面只希望幾個特定的使用者瀏覽。對於這樣的訪問許可權控制,應該如何實現呢?攔截器就可以實現上述需求。在 Struts2 框架中,攔截器是其重要的組成部分,Spring MVC 框架也提供了攔截器功能。
Spring MVC 的攔截器(Interceptor)與 Java Servlet 的過濾器(Filter)類似,它主要用於攔截使用者的請求並做相應的處理,通常應用在許可權驗證、記錄請求資訊的日誌、判斷使用者是否登入等功能上。
攔截器的定義
在 Spring MVC 框架中定義一個攔截器需要對攔截器進行定義和配置,主要有以下 2 種方式。
- 通過實現 HandlerInterceptor 介面或繼承 HandlerInterceptor 介面的實現類(例如 HandlerInterceptorAdapter)來定義;
- 通過實現 WebRequestInterceptor 介面或繼承 WebRequestInterceptor 介面的實現類來定義。
本節以實現 HandlerInterceptor 介面的定義方式為例講解自定義攔截器的使用方法。示例程式碼如下。
package net.biancheng.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class TestInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion方法在控制器的處理請求方法執行完成後執行,即檢視渲染結束之後執行"); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle方法在控制器的處理請求方法呼叫之後,解析檢視之前執行"); } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle方法在控制器的處理請求方法呼叫之前執行"); return false; } }
上述攔截器的定義中實現了 HandlerInterceptor 介面,並實現了介面中的 3 個方法,說明如下。
- preHandle( ):該方法在控制器的處理請求方法前執行,其返回值表示是否中斷後續操作,返回 true 表示繼續向下執行,返回 false 表示中斷後續操作。
- postHandle( ):該方法在控制器的處理請求方法呼叫之後、解析檢視之前執行,可以通過此方法對請求域中的模型和檢視做進一步的修改。
- afterCompletion( ):該方法在控制器的處理請求方法執行完成後執行,即檢視渲染結束後執行,可以通過此方法實現一些資源清理、記錄日誌資訊等工作。
攔截器的配置
讓自定義的攔截器生效需要在 Spring MVC 的配置檔案中進行配置,配置示例程式碼如下:
<!-- 配置攔截器 --> <mvc:interceptors> <!-- 配置一個全域性攔截器,攔截所有請求 --> <bean class="net.biancheng.interceptor.TestInterceptor" /> <mvc:interceptor> <!-- 配置攔截器作用的路徑 --> <mvc:mapping path="/**" /> <!-- 配置不需要攔截作用的路徑 --> <mvc:exclude-mapping path="" /> <!-- 定義<mvc:interceptor>元素中,表示匹配指定路徑的請求才進行攔截 --> <bean class="net.biancheng.interceptor.Interceptor1" /> </mvc:interceptor> <mvc:interceptor> <!-- 配置攔截器作用的路徑 --> <mvc:mapping path="/gotoTest" /> <!-- 定義在<mvc:interceptor>元素中,表示匹配指定路徑的請求才進行攔截 --> <bean class="net.biancheng.interceptor.Interceptor2" /> </mvc:interceptor> </mvc:interceptors>
在上述示例程式碼中,元素說明如下。
- <mvc:interceptors>:該元素用於配置一組攔截器。
- <bean>:該元素是 <mvc:interceptors> 的子元素,用於定義全域性攔截器,即攔截所有的請求。
- <mvc:interceptor>:該元素用於定義指定路徑的攔截器。
- <mvc:mapping>:該元素是 <mvc:interceptor> 的子元素,用於配置攔截器作用的路徑,該路徑在其屬性 path 中定義。path 的屬性值為
/**
時,表示攔截所有路徑,值為/gotoTest
時,表示攔截所有以/gotoTest
結尾的路徑。如果在請求路徑中包含不需要攔截的內容,可以通過 <mvc:exclude-mapping> 子元素進行配置。
需要注意的是,<mvc:interceptor> 元素的子元素必須按照 <mvc:mapping.../>、<mvc:exclude-mapping.../>、<bean.../> 的順序配置。
示例
下面通過攔截器來完成一個使用者登入許可權驗證的 Web 應用,具體要求如下:只有成功登入的使用者才能訪問系統的主頁面
main.jsp,如果沒有成功登入而直接訪問主頁面,則攔截器將請求攔截,並轉發到登入頁面
login.jsp。當成功登入的使用者在系統主頁面中單擊“退出”連結時回到登入頁面。
本節示例基於《@Controller和@RequestMapping註解》一節的 springmvcDemo2 應用實現,具體實現步驟如下。
1)建立 POJO 類
在 springmvcDemo2 的 net.biancheng.pojo 包中建立 User 類,程式碼如下。
package net.biancheng.po; public class User { private String name; private String pwd; /**省略setter和getter方法*/ }
2)建立控制器類
在 springmvcDemo2的 net.biancheng.controller 包中建立控制器類 UserController,程式碼如下。
package net.biancheng.controller; import javax.servlet.http.HttpSession; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import net.biancheng.po.User; @Controller public class UserController { /** * 登入頁面初始化 */ @RequestMapping("/toLogin") public String initLogin() { return "login"; } /** * 處理登入功能 */ @RequestMapping("/login") public String login(User user, Model model, HttpSession session) { System.out.println(user.getName()); if ("bianchengbang".equals(user.getName()) && "123456".equals(user.getPwd())) { // 登入成功,將使用者資訊儲存到session物件中 session.setAttribute("user", user); // 重定向到主頁面的跳轉方法 return "redirect:main"; } model.addAttribute("msg", "使用者名稱或密碼錯誤,請重新登入! "); return "login"; } /** * 跳轉到主頁面 */ @RequestMapping("/main") public String toMain() { return "main"; } /** * 退出登入 */ @RequestMapping("/logout") public String logout(HttpSession session) { // 清除 session session.invalidate(); return "login"; } }
3)建立攔截器類
在 springmvcDemo2的 net.biancheng.interceptor 包中建立攔截器類 LoginInterceptor,程式碼如下。
package net.biancheng.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 獲取請求的URL String url = request.getRequestURI(); // login.jsp或登入請求放行,不攔截 if (url.indexOf("/toLogin") >= 0 || url.indexOf("/login") >= 0) { return true; } // 獲取 session HttpSession session = request.getSession(); Object obj = session.getAttribute("user"); if (obj != null) return true; // 沒有登入且不是登入頁面,轉發到登入頁面,並給出提示錯誤資訊 request.setAttribute("msg", "還沒登入,請先登入!"); request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); return false; } @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { // TODO Auto-generated method stub } @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { // TODO Auto-generated method stub } }
4)配置攔截器
在 WEB-INF 目錄下建立配置檔案 springmvc-servlet.xml 和 web.xml。web.xml 的程式碼和 springmvcDemo2 一樣,這裡不再贅述。在 springmvc-servlet.xml 檔案中配置攔截器 LoginInterceptor,程式碼如下。
<!-- 配置攔截器 --> <mvc:interceptors> <mvc:interceptor> <!-- 配置攔截器作用的路徑 --> <mvc:mapping path="/**" /> <bean class="net.biancheng.interceptor.LoginInterceptor" /> </mvc:interceptor> </mvc:interceptors>
5)建立檢視 JSP 頁面
在 WEB-INF 目錄下建立資料夾 jsp,並在該資料夾中建立 login.jsp 和 main.jsp。
login.jsp 的程式碼如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>使用者登入</title> </head> <body> ${msg } <form action="${pageContext.request.contextPath }/login" method="post"> 使用者名稱:<input type="text" name="name" /><br> 密碼:<input type="password" name="pwd" /><br> <input type="submit" value="登入" /> </form> </body> </html>
main.jsp 的程式碼如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>首頁</title> </head> <body> 歡迎 ${user.name },登入程式設計幫!<br /> <a href="${pageContext.request.contextPath }/logout">退出</a> </body> </html>
6)釋出並測試應用
首先將 springmvcDemo2 應用釋出到 Tomcat 伺服器並啟動 Tomcat 伺服器,然後通過地址“http://localhost:8080/springmvcDemo2/main”測試應用,執行效果如圖 1 所示。
圖 1沒有登入直接訪問主頁面的效果
從圖 1
可以看出,當用戶沒有登入而直接訪問系統主頁面時請求將被登入攔截器攔截,返回到登入頁面,並提示資訊。如果使用者在使用者名稱框中輸入“bianchengbang”,在密碼框中輸入“123456”,單擊“登入”按鈕後瀏覽器的顯示結果如圖
2 所示。如果輸入的使用者名稱或密碼錯誤,瀏覽器的顯示結果如圖 3 所示。
圖 2成功登入的效果
圖 3使用者名稱或密碼錯誤
當單擊圖 2 中的“退出”連結後,系統將從主頁面返回到登入頁面。
from
http://c.biancheng.net/spring_mvc/interceptor.html