SpringMVC提供了攔截器Interceptor,可以用於驗證用戶登錄,它跟過濾器是有區別的,攔截器是Spring提供的,而過濾器是Servlet提供的。
使用攔截器的條件
使用攔截器前面需要進行配置,包括導包、web.xml中配置DispatcherServlet,Spring啟動讀取文件中配置組件掃描、注解驅動、視圖解析器和攔截器。其他就是需要寫一個控制器用來進行請求分發處理,還有自定義攔截器。
自定義攔截器
自定義攔截器需要實現HandlerInterceptor接口,實現接口定義的方法。
1 package Interceptors; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletResponse; 5 6 import org.springframework.web.servlet.HandlerInterceptor; 7 import org.springframework.web.servlet.ModelAndView; 8 /** 9 * 攔截器類需要實現HandlerInterceptor接口 10 * @author yangchaolin 11 * 12 */ 13 public class SomeInterceptor implements HandlerInterceptor{ 14 /** 15 * DispatcherServlet在收到請求后,會先調用preHandler方法,如果該方法的返回值為true,則繼續向后調用Controller的方法 16 * 如果返回值是false,則中斷請求 17 * 18 * DispatcherServlet,攔截器、Controller會共享同一個request,response 19 * handler:Controller的方法對象,利用了java反射機制,后面了解學習 20 */ 21 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 22 throws Exception { 23 System.out.println("攔截器的preHandle方法執行了"); 24 return true; 25 } 26 /** 27 * 是Controller處理完后,在將ModelAndView返回給前端控制器DispatcherServlet之前,執行的方法 28 * 可以在該方法里,修改ModelAndView的處理結果 29 */ 30 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, 31 ModelAndView modelAndView) throws Exception { 32 System.out.println("攔截器的postHandle方法執行了"); 33 } 34 /** 35 * 最后執行 36 * ex:是處理器Controller所拋出的異常 37 * 可以寫一個攔截器專門處理Controller拋出的異常 38 */ 39 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 40 throws Exception { 41 System.out.println("攔截器的afterCompletion方法執行了"); 42 } 43 }
Controller
寫一個Controller來測試攔截器方法的執行順序,里面為了測試多級目錄寫了兩個方法。
1 package Controller; 2 3 import org.springframework.stereotype.Controller; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 6 @Controller 7 public class HelloController { 8 @RequestMapping("/hello.do") 9 public String hello() { 10 System.out.println("控制器的hello()方法執行了"); 11 return "hello"; 12 13 } 14 /** 15 * 如果路徑加一個demo,然而攔截器mapping還是/*的話,將不會攔截 16 * 如果想實現各種路徑的攔截,不論幾層都能實現攔截效果的話,需要將mapping修改為/** 17 * @return 18 */ 19 @RequestMapping("/demo/hello.do") 20 public String hello1() { 21 System.out.println("hello1()"); 22 return "hello"; 23 } 24 }
配置攔截器
在Spring啟動默認讀取文件applicationContext.xml文件中添加攔截器配置,這里只設置了一個攔截器,可以配置多個攔截器,執行順序從上往下攔截。
1 <!-- 設置攔截器Interceptor --> 2 <!-- 如果想要攔截所有的請求,path應該寫成/** --> 3 <mvc:interceptors> 4 <!-- 第一個攔截器 --> 5 <mvc:interceptor> 6 <mvc:mapping path="/**"/> 7 <bean class="Interceptors.SomeInterceptor"></bean> 8 </mvc:interceptor> 9 <!-- 后面可以配置多個攔截器,攔截順序從上到下 --> 10 </mvc:interceptors>
運行結果,只展示hello.do請求,多級目錄/demo/hello.do也可以攔截。
控制台輸出情況
可以看出來攔截器方法的執行順序
(1)DispatcherServlet接受到請求后,首先會根據HandlerMapping配置請求的結果(這里使用注解@RequestMapping來完成),查看是否有對應請求Mapping
(2)如果沒有匹配路徑不再往下執行,如果有請求匹配的話,會先執行攔截器方法preHandle,返回true則繼續執行Controller的方法
(3)Controller執行完邏輯准備將結果(String或者ModelAndView)返回給前端控制器前,先執行postHandle方法,如果有異常,接下來在afterCompletion方法中進行處理,最后將結果返回給前端控制器
攔截器用於登錄驗證
接下來使用攔截器,在前面登錄案例的基礎上,添加一個用戶登錄驗證,即登錄成功的才跳轉到后面頁面,否則返回到登錄頁面,前期配置參考博文。
(1)JSP准備
登錄頁面、主頁,sub頁,主頁用於登錄驗證,主頁直接登錄,sub頁直接登錄。

1 <%@page pageEncoding="utf-8"%> 2 <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 3 <!doctype html> 4 <html> 5 <head> 6 <title>歡迎登錄Color Filter-JavaScript登錄驗證</title> 7 <meta charset="utf-8"> 8 <link rel="stylesheet" type="text/css" href="css/login.css" /> 9 <!-- <script type="text/javascript" src="script/login.js"></script> --> 10 </head> 11 <body> 12 <!--logo區+段落區--> 13 <div class="logo"> 14 <img src="image/LogoCF.jpg"> 15 <p id="logoDesc">Sign in to Color Filter</p> 16 </div> 17 <!--主體登錄區--> 18 <div class="main"> 19 <div id="login"> 20 <!--5行2列--> 21 <!--表單用於輸入登錄信息,提交給服務器 --> 22 <!-- 23 onsubmit是表單提交事件,在點擊提交按鈕時觸發。觸發時先調用onsubmit內的方法,若該方法返回true則會自動提交表單 24 返回false則不提交,此處聲明的方法可以起到攔截提交的作用,避免賬號名和密碼都不對時也能提交到服務器 25 onsubmit="return ((check_username()+check_pwd())==2)" 26 --> 27 <form action="logincheck.do" method="get"> 28 <table> 29 <tr> 30 <td colspan="2" style="text-align: left;text-indent: 29px">Username or email address</td> 31 </tr> 32 <tr> 33 <!-- 增加切換光標確認用戶名格式是否正確 --> 34 <td colspan="2"><input type="text" name="user" id="username" onblur="check_username();" value="${username }"></td> 35 </tr> 36 <tr> 37 <td style="text-align: left;text-indent: 29px;width:15%">Password</td> 38 <td style="text-align: left;text-indent: 0px;width:85%"><a href="#">Forget password?</a></td> 39 </tr> 40 <tr> 41 <td colspan="2" style="width: 345px"><input type="password" name="pwd" id="pwd" onblur="check_pwd();" value="${password }"></td> 42 </tr> 43 <tr> 44 <td colspan="2" style="text-align: left;text-indent: 29px">Security Code</td> 45 </tr> 46 <tr> 47 <td style="padding-left:32px;width:60%;text-align:left;"><input type="text" name="valicode" id="valicode"/></td> 48 <!-- <td style="padding-top:3px;width:40%;text-align:right;padding-right:32px"><img alt="驗證碼" src="createIMG.do" onclick="this.setAttribute('src','createIMG.do?x='+Math.random())"></td> --> 49 <!-- 添加登錄失敗提示 --> 50 <td>${login_fail}</td> 51 </tr> 52 <tr> 53 <td colspan="2" id="buttontd"><input type="submit" name="btn" value="Sign in" id="button"></td> 54 </tr> 55 </table> 56 </form> 57 </div> 58 <div id="regist"> 59 <p style="text-align: center;">New to Color Filter? <a href="#">Create an account.</a></p> 60 </div> 61 </div> 62 <!--版權、隱私、法律相關--> 63 <div class="foot"> 64 <p><a href="#">Terms</a> <a href="#">Privacy</a> <a href="#">Security</a> Contact Color Filter</p> 65 </div> 66 </body> 67 68 </html>

1 <%@page pageEncoding="utf-8" contentType="text/html; charset=utf-8"%> 2 <h1>歡迎登陸</h1><br/> 3 <!-- 得到服務器返回的user參數,采用el表達式獲取 --> 4 ${user } 5 <!-- 攔截器測試 --> 6 <div> 7 <form action="toSubPage.do" method="post"> 8 <a href="javascript:document.forms[0].submit();">點我繼續訪問</a> 9 </form> 10 </div>

1 <%@page pageEncoding="utf-8" contentType="text/html; charset=utf-8"%> 2 <h1>歡迎來到subPage</h1>
(2)自定義攔截器類及攔截器配置
攔截器類

1 package com.boe.interceptor; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletResponse; 5 6 import org.springframework.web.servlet.HandlerInterceptor; 7 import org.springframework.web.servlet.ModelAndView; 8 9 /** 10 * 配置自定義攔截器 11 * @author yangchaolin 12 */ 13 public class LoginInterceptor implements HandlerInterceptor{ 14 //在一次請求中,DispatcherServlet、攔截器和Controller共用一個request和response 15 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 16 throws Exception { 17 //進行登錄驗證攔截,除了toLogin.do,logincheck.do都進行攔截 18 Object obj=request.getSession().getAttribute("loginData"); 19 if(obj==null) { 20 //返回登錄頁面 21 System.out.println("沒有登錄,進行攔截"); 22 //重定向,使用不帶"/",相對路徑 23 response.sendRedirect("toLogin.do"); 24 return false; 25 }else { 26 System.out.println("登錄成功,繼續訪問"); 27 return true; 28 } 29 } 30 31 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, 32 ModelAndView modelAndView) throws Exception { 33 // TODO Auto-generated method stub 34 35 } 36 37 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 38 throws Exception { 39 // TODO Auto-generated method stub 40 41 } 42 }
applicationContext.xml文件中配置攔截器
1 <!-- 添加攔截器,不攔截進入登錄頁面,登錄驗證 --> 2 <mvc:interceptors> 3 <mvc:interceptor> 4 <mvc:mapping path="/**"/> 5 <mvc:exclude-mapping path="/toLogin.do"/> 6 <mvc:exclude-mapping path="/logincheck.do"/> 7 <bean class="com.boe.interceptor.LoginInterceptor"></bean> 8 </mvc:interceptor> 9 </mvc:interceptors>
(3)修改LoginController控制器方法,登錄成功后將用戶信息保存到session中。

1 package com.boe.controller; 2 3 import javax.annotation.Resource; 4 import javax.servlet.http.HttpSession; 5 6 import org.springframework.stereotype.Controller; 7 import org.springframework.ui.ModelMap; 8 import org.springframework.web.bind.annotation.RequestMapping; 9 10 import com.boe.entity.Admin; 11 import com.boe.entity.userInfo; 12 import com.boe.exception.ApplicationException; 13 import com.boe.service.LoginService; 14 15 @Controller 16 public class mainController { 17 @Resource(name="loginServiceImpl") 18 private LoginService service; 19 20 //去到登錄頁面 21 @RequestMapping("/toLogin.do") 22 public String toLogin() { 23 System.out.println("去到登錄頁面"); 24 return "login"; 25 } 26 27 //驗證登錄 28 @RequestMapping("/logincheck.do") 29 public String login(userInfo data,ModelMap mm,HttpSession session) { 30 System.out.println("登錄驗證"); 31 System.out.println("用戶名為:"+data.getUser()); 32 System.out.println("密碼為:"+data.getPwd()); 33 //處理業務層異常 34 try { 35 Admin admin=service.checkLogin(data.getUser(), data.getPwd()); 36 if(admin!=null) { 37 System.out.println("登錄成功"); 38 mm.addAttribute("user",data.getUser()); 39 //登錄成功后,將admin信息存入session 40 session.setAttribute("loginData", admin); 41 } 42 }catch(Exception e) { 43 //應用錯誤,即用戶填寫錯誤 44 if(e instanceof ApplicationException) { 45 mm.addAttribute("login_fail", e.getMessage()); 46 mm.addAttribute("username",data.getUser()); 47 mm.addAttribute("password", data.getPwd()); 48 return "login"; 49 } 50 //系統錯誤 51 return "loginNG"; 52 } 53 return "loginOK"; 54 } 55 56 //直接進入主頁,攔截器測試 57 @RequestMapping("/loginOK.do") 58 public String toMainPage() { 59 System.out.println("准備進入loginOK主頁"); 60 return "loginOK"; 61 } 62 //直接進入sub頁,攔截器測試 63 @RequestMapping("/toSubPage.do") 64 public String toSubPage() { 65 System.out.println("准備進入subPage副頁"); 66 return "subPage"; 67 } 68 }
(4)測試不登錄的情況下,進入loginOK頁面,發現被攔截了,並提示沒有登錄。
控制台情況,顯示執行了自定義攔截器的preHandle方法,並返回false,不再執行controller里的方法,進行重定向到登錄頁面。
(5)測試登錄的情況下,訪問loginOK.do和toSubPage.do 。
控制台情況,可以看出驗證登錄是沒有攔截的,登錄成功后進入主頁,然后點擊鏈接時就會被攔截到,因為有登錄所以攔截器的preHandle方法返回true並打印出了"登錄成功,繼續訪問",后進入LoginController繼續處理邏輯,打印出了"准備進入subPage副頁",並返回String結果給前端控制器,讓前端控制器分發視圖解析器最后完成副頁展示。
總結
攔截器可以用於登錄驗證,但是其屬於Spring,如果前端更換框架則不再生效,而過濾器是servlet范疇,配置了過濾器無論怎么更換都會過濾,相比較來說過濾器范圍更廣。
參考博文:
(1)https://www.cnblogs.com/youngchaolin/p/11354307.html springMVC完成登錄頁面
(2)https://www.cnblogs.com/youngchaolin/p/10549020.html 過濾器和攔截器
(3)https://blog.csdn.net/koflance/article/details/79635240 URI、URL和URN