Spring MVC中的攔截器(Interceptor)相似於Servlet中的過濾器(Filter),它主要用於攔截用戶請求並做相應的處理。例如經過攔截器能夠進行權限驗證、記錄請求信息的日誌、判斷用戶是否登陸等。css
1.建立攔截器實現HandlerInterceptor接口
2.配置攔截器
3.測試攔截器的攔截效果html
package com.pjh.HandleInterceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { /*目標方法執行前執行*/ /*false表明不放行,true表明放行*/ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("第一個:preHandle"); return false; } /*目標方法執行以後,視圖返回以前執行*/ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("第一個:postHandle"); } /*所有流程執行完畢後執行*/ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("第一個:afterCompletion"); } }
方法介紹java
preHandle() 方法:該方法會在控制器方法前執行,其返回值表示是否中斷後續操做。當其返回值爲true時,表示繼續向下執行;
當其返回值爲false時,會中斷後續的全部操做(包括調用下一個攔截器和控制器類中的方法執行等)。web
postHandle()方法:該方法會在控制器方法調用以後,且解析視圖以前執行。能夠經過此方法對請求域中的模型和視圖作出進一步的修改。spring
afterCompletion()方法:該方法會在整個請求完成,即視圖渲染結束以後執行。能夠經過此方法實現一些資源清理、記錄日誌信息等工做。apache
開發攔截器就像開發servlet或者filter同樣,都須要在配置文件進行配置,配置代碼以下:編程
<mvc:interceptors> <mvc:interceptor> <!--配置攔截器做用路徑--> <mvc:mapping path="/**"/> <!--定義在<mvc:interceptor>下面的表示匹配指定路徑的請求才進行攔截--> <!--這裏是對全部目標方法都進行攔截--> <bean class="com.pjh.HandleInterceptor.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors>
上面的代碼中,mvc:interceptors元素用於配置一組攔截器,基子元素
注意:mvc:interceptor中的子元素必須按照上述代碼中的配置順序進行編寫,即mvc:mapping mvc:exclude-mapping
在運行程序時,攔截器的執行是有必定順序的,該順序與配置文件中所定義的攔截器的順序相關。
單個攔截器,在程序中的執行流程以下圖所示:瀏覽器
1.程序先執行preHandle()方法,若是該方法的返回值爲true,則程序會繼續向下執行處理器中的方法,不然將再也不向下執行。tomcat
2.在業務處理器(即控制器Controller類)處理完請求後,會執行postHandle()方法,而後會經過DispatcherServlet向客戶端返回響應。
3.在DispatcherServlet處理完請求後,纔會執行afterCompletion()方法。
定義一個攔截器,訪問資源後觀察控制檯輸出
package com.pjh.HandleInterceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { /*目標方法執行前執行*/ /*false表明不放行,true表明放行*/ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("第一個:preHandle"); return false; } /*目標方法執行以後,視圖返回以前執行*/ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("第一個:postHandle"); } /*所有流程執行完畢後執行*/ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("第一個:afterCompletion"); } }
多個攔截器(假設有兩個攔截器Interceptor1和Interceptor2,而且在配置文件中, Interceptor1攔截器配置在前),在程序中的執行流程以下圖所示:
從圖能夠看出,當有多個攔截器同時工做時,它們的preHandle()方法會按照配置文件中攔截器的配置順序執行,而它們的postHandle()方法和afterCompletion()方法則會按照配置順序的反序執行。
測試案例
mvc配置文件中的數據
<!--配置攔截器--> <mvc:interceptors> <!--第一個攔截器--> <mvc:interceptor> <!--配置攔截器做用路徑--> <mvc:mapping path="/**"/> <!--定義在<mvc:interceptor>下面的表示匹配指定路徑的請求才進行攔截--> <!--這裏是對全部目標方法都進行攔截--> <bean class="com.pjh.HandleInterceptor.MyInterceptor"/> </mvc:interceptor> <!--第二個攔截器--> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.pjh.HandleInterceptor.MyInterceptor2"/> </mvc:interceptor> </mvc:interceptors>
第一個攔截器中的代碼
/*目標方法執行前執行*/ /*false表明不放行,true表明放行*/ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("第一個:preHandle"); return true; } /*目標方法執行以後,視圖返回以前執行*/ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("第一個:postHandle"); } /*所有流程執行完畢後執行*/ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("第一個:afterCompletion"); }
**
**
**
第二個攔截器中的代碼
package com.pjh.HandleInterceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor2 implements HandlerInterceptor { /*目標方法執行前執行*/ /*false表明不放行,true表明放行*/ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("第二個:preHandle"); return true; } /*目標方法執行以後,視圖返回以前執行*/ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("第二個:postHandle"); } /*所有流程執行完畢後執行*/ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("第二個:afterCompletion"); } }
訪問資源後的控制檯輸出
攔截器經過判斷get方法中的參數來判斷是否跳轉到頁面的案例
攔截器代碼配置
package com.pjh.HandleInterceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { /*目標方法執行前執行*/ /*false表明不放行,true表明放行*/ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("第一個:preHandle"); String param = request.getParameter("param"); /*若是參數爲false返回success頁面*/ /*若是參數不爲false返回error頁面*/ if ("yes".equals(param)){ return true; }else { request.getRequestDispatcher("error.jsp").forward(request,response); return false; } } /*目標方法執行以後,視圖返回以前執行*/ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("第一個:postHandle"); } /*所有流程執行完畢後執行*/ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("第一個:afterCompletion"); } }
當get方法所帶參數爲yes時
當get方法所帶參數不爲yes時
以訪問一個後臺管理系統爲例,若是用戶登入了則讓其能夠訪問後臺管理系統,若是用戶沒有登入則在用戶點擊任意菜單時都跳轉到登入頁面
點擊側邊欄的任何一個按鈕均跳轉到登入頁面
<!--配置攔截器--> <mvc:interceptors> <mvc:interceptor> <!--配置攔截路徑,對哪些方法進行攔截操做--> <mvc:mapping path="/**"/> <!--配置攔截路徑對哪些方法不進行攔截操做,應該將login函數放行,否則就一直卡在登入頁面--> <mvc:exclude-mapping path="/user/login"/> <!--配置攔截器類的所在位置--> <bean class="com.pjh.Interceptor.PrivilegeInterceptor"/> </mvc:interceptor> </mvc:interceptors>
package com.pjh.Interceptor; import com.pjh.domain.User; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.print.DocFlavor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class PrivilegeInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { /*若是session域中的user對象爲空則跳轉到登入頁面說明用戶沒有登入或則登入失敗*/ HttpSession session = request.getSession(); User user = (User) session.getAttribute("user"); System.out.println("攔截器:"+user); if (user==null){ response.sendRedirect(request.getContextPath()+"/login.jsp"); return false; } /*用戶存在放行*/ return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle"); } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion"); } }
調用Service層函數判斷是否存在該用戶
@Autowired private UserService userService; @RequestMapping("/login") public String login(String username, String password, HttpSession session){ System.out.println(username); System.out.println(password); User user =userService.login(username,password); if (user!=null){ session.setAttribute("user",user); System.out.println(user); return "redirect:/index.jsp"; }else { return "redirect:/login.jsp"; } }
public User login(String username, String password) { User user=userDao.findPasswordAndUsername(username,password); return user; }
注意這裏要捕獲異常不然當查詢到用戶爲空的時候會拋出異常
public User findPasswordAndUsername(String username, String password) { try{ User user = jdbcTemplate.queryForObject("select * from sys_user where username=? and password=?", new BeanPropertyRowMapper<User>(User.class), username, password); System.out.println(user); return user; }catch (EmptyResultDataAccessException e){ return null; } }
通過以上設置只有用戶輸入正確的用戶名及其密碼以後才能夠進入後臺管理頁面
顧名思義即過濾掉一些東西,好比咱們經歷的高考中考都是過濾器,他過濾掉一些在學習這一方面不是很好的人,而那些成績好的人則升入高中,大學。
可是java中的過濾器與生活中的過濾器的做用是相差無幾的,即按照制定的一些規則來控制一些對象
過濾器是出於客戶端與服務器端之間的一道過濾網,在訪問資源以前,經過一系列的過濾器對請求進行修改,判斷等。把不符合規則的請求在中途攔截或修改,攔截或修改響應
自動登陸
統一設置編碼格式
訪問權限控制
敏感字符過濾等
<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>servlet-api</artifactId> <version>6.0.29</version> </dependency> <dependency> <groupId>org.mortbay.jetty</groupId> <artifactId>servlet-api-2.5</artifactId> <version>6.1.7</version> </dependency>
Filter類代碼以下
**
package com.pjh; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; /*在執行全部資源以前都會執行該過濾器*/ @WebFilter("/*") public class MyFilter1 implements Filter { /*在服務器啓動後,會建立Filter對象,而後調用init方法。只執行一次。用於加載資源*/ public void init(FilterConfig filterConfig) throws ServletException { System.out.println("初始化"); } /*:每一次請求被攔截資源時,會執行。執行屢次*/ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("執行過濾器操做"); /*放行操做*/ filterChain.doFilter(servletRequest, servletResponse); } /*在服務器關閉後,Filter對象被銷燬。若是服務器是正常關閉,則會執行destroy方法。只執行一次。用於釋放資源*/ public void destroy() { System.out.println("過濾器銷燬操做"); } }
<filter> <filter-name>MyFilter1</filter-name> <!--過濾器所在的類的路徑--> <filter-class>com.pjh.MyFilter1</filter-class> </filter> <filter-mapping> <!--攔截路徑配置--> <filter-name>MyFilter1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
在服務器啓動後,會建立Filter對象,而後調用init方法。只執行一次。用 於加載資源
**
每一次請求被攔截資源時,會執行。執行屢次
在服務器關閉後,Filter對象被銷燬。若是服務器是正常關閉,則會執 行destroy方法。只執行一次。用於釋放資源
** 1. 具體資源路徑:**
/index.jsp 只有訪問index.jsp資源時,過濾器纔會被執行
2. 攔截目錄:
/user/* 訪問/user下的全部資源時,過濾器都會被執行
3. 後綴名攔截:
*.jsp 訪問全部後綴名爲jsp資源時,過濾器都會被執行
4. 攔截全部資源:
/* 訪問全部資源時,過濾器都會被執行
*** 註解配置:** * 設置dispatcherTypes屬性 1. REQUEST:默認值。瀏覽器直接請求資源 2. FORWARD:轉發訪問資源 3. INCLUDE:包含訪問資源 4. ERROR:錯誤跳轉資源 5. ASYNC:異步訪問資源
package com.pjh; import javax.print.DocFlavor; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; /*在執行全部資源以前,並且是直接訪問該資源的時候纔會執行該過濾器*/ @WebFilter(value = "/*",dispatcherTypes =DispatcherType.REQUEST ) public class MyFilter1 implements Filter { /*在服務器啓動後,會建立Filter對象,而後調用init方法。只執行一次。用於加載資源*/ public void init(FilterConfig filterConfig) throws ServletException { System.out.println("初始化"); } /*:每一次請求被攔截資源時,會執行。執行屢次*/ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("執行過濾器操做"); /*放行操做*/ filterChain.doFilter(servletRequest, servletResponse); } /*在服務器關閉後,Filter對象被銷燬。若是服務器是正常關閉,則會執行destroy方法。只執行一次。用於釋放資源*/ public void destroy() { System.out.println("過濾器銷燬操做"); } }
* 設置<dispatcher></dispatcher>標籤便可
例
<filter> <filter-name>MyFilter1</filter-name> <!--過濾器所在的類的路徑--> <filter-class>com.pjh.MyFilter1</filter-class> </filter> <filter-mapping> <!--攔截路徑配置--> <filter-name>MyFilter1</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping>
看圖就行,很好理解
1. 過濾器1 2. 過濾器2 3. 資源執行 4. 過濾器2 5. 過濾器1
註解配置:按照類名的字符串比較規則比較,值小的先執行
如: AFilter 和 BFilter,AFilter就先執行了
web.xml配置:
以訪問一個後臺管理系統爲例,若是用戶登入了則讓其能夠訪問後臺管理系統,若是用戶沒有登入則不能訪問任何的該網站頁面,而且自動跳轉到登入頁面,在登入後才能夠訪問其餘頁面
點擊側邊欄的任何一個按鈕均跳轉到登入頁面
package com.pjh.Filter; import com.pjh.domain.User; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.io.IOException; public class LoginFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { System.out.println("過濾啓動"); } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("過濾器執行"); System.out.println(servletRequest); /*強制轉換*/ HttpServletRequest request= (HttpServletRequest)servletRequest; /*獲取請求資源的路徑*/ String uri = request.getRequestURI(); /*判斷是不是包含登入資源的相關路徑要注意排除掉 css/js/圖片/驗證碼等資源*/ if (uri.contains("/login") ||uri.contains("/login.jsp")|| uri.contains("/loginServlet") || uri.contains("/css/") || uri.contains("/js/") || uri.contains("/fonts/") || uri.contains("/checkCodeServlet") ||uri.contains("/img/")){ /*是訪問登入相關的路徑,放行*/ filterChain.doFilter(servletRequest,servletResponse); }else{ /*不是登入的相關路徑,看看用戶有沒有登入*/ HttpSession session = request.getSession(); /*從session域中獲取user,看看有沒有登入*/ User user = (User)session.getAttribute("user"); System.out.println(user); if (user==null){ /*未登入跳轉到登入頁面*/ request.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse); }else{ /*已經登入放行*/ filterChain.doFilter(servletRequest, servletResponse); } } } public void destroy() { System.out.println("過濾器銷燬"); } }
<filter> <filter-name>filter1</filter-name> <!--過濾器所在的位置--> <filter-class>com.pjh.Filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>filter1</filter-name> <!--過濾器對全部資源生效--> <url-pattern>/*</url-pattern> </filter-mapping>
通過以上配置用戶只有在登入以後才能夠對資源進行訪問了
好比在王者榮耀當中常常有低素質的人在裏面狂噴別人,滿嘴污穢詞彙,這個時候爲了營造良好的遊戲氛圍,就要使用到敏感詞彙過濾的技術,將罵人的詞彙變成「****」
這時候咱們就要使用過濾器了,在過濾器中對這些敏感詞彙進行等一系列操做
下面經過一張圖來說解
**
好比咱們的敏感詞彙爲「壞蛋」,若是咱們輸入「你是壞蛋」那麼過濾後的內容就爲「你是**」,在過濾器中因爲reqest對象沒有setParemeter操做,因此咱們只能對request對象的getParameter方法進行加強,而且產生一個新的Parameter對象,並在新的request對象中加入過濾後的詞彙,供其餘方法調用
需求對如下敏感詞彙進行過濾,並將敏感詞彙替換爲「」*
笨蛋 傻瓜
**
**
分析:
1.對request對象進行加強,加強獲取參數的相關方法
2.放行,傳遞代理對象
Filter函數代碼以下
package com.pjh.Filter; import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ; import javax.servlet.*; import java.io.*; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; public class SensitiveWordFilter implements Filter { /*建立敏感詞聚集合*/ private List<String> list = new ArrayList<String>(); public void init(FilterConfig filterConfig) throws ServletException { /*讀取相關的敏感詞彙文件並存入對應的集合中*/ try{ /*獲取文件的真實路徑*/ ServletContext servletContext = filterConfig.getServletContext(); InputStream path = this.getClass().getResourceAsStream("/SensitiveWord.txt"); /*讀取文件*/ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(path,"UTF-8")); /*將文件的每一行數據添加到list集合中*/ String line=null; while ((line=bufferedReader.readLine())!=null){ list.add(line); } bufferedReader.close(); // System.out.println(list); }catch (Exception e){ System.out.println(e); } } public void doFilter(final ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("Filter1:"+servletRequest); ServletRequest proxy_req= (ServletRequest) Proxy.newProxyInstance(servletRequest.getClass().getClassLoader(), servletRequest.getClass().getInterfaces(), new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*1.判斷是不是getParameter方法 2. 加強getParameter方法 */ if (method.getName().equals("getParameter")){ /*加強返回值*/ /*獲取返回值*/ String value= (String)method.invoke(servletRequest,args); /*遍歷集合判斷字符串中是否存在敏感詞彙*/ if (value!=null){ for (String s : list) { if (value.contains(value)){ value=value.replace(s,"***"); } } } return value; } return method.invoke(servletRequest,args); } }); System.out.println("list集合數據:"+list); System.out.println("filter2proxy_req:"+proxy_req); /*放行*/ filterChain.doFilter(proxy_req,servletResponse); } public void destroy() { System.out.println("銷燬方法"); } }
在web.xml中設置攔截路徑
<!--設置敏感詞彙過濾器--> <filter> <filter-name>filter2</filter-name> <filter-class>com.pjh.Filter.SensitiveWordFilter</filter-class> </filter> <filter-mapping> <filter-name>filter2</filter-name> <url-pattern>/filter/*</url-pattern> </filter-mapping>
Controller類代碼
package com.pjh.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.UnsupportedEncodingException; /*設置編碼格式*/ @Controller @RequestMapping(value = "/filter",produces = "text/html;charset=utf-8") public class TestFilterController { @RequestMapping("/test1") @ResponseBody public String test1(ServletRequest request, ServletResponse response) throws UnsupportedEncodingException { System.out.println("test1:"+request); String id = request.getParameter("id"); System.out.println("test1ID:"+id); return "你的id是"+id; } }
未設置過濾器前
**
設置過濾器後
**
依賴於servlet容器。在實現上基於函數回調,能夠對幾乎全部請求進行過濾,可是缺點是一個過濾器實例只能在容器初始化時調用一次。使用過濾器的目的是用來作一些過濾操做,獲取咱們想要獲取的數據,好比:在過濾器中修改字符編碼;在過濾器中修改HttpServletRequest的一些參數,包括:過濾低俗文字、危險字符等
依賴於web框架,在SpringMVC中就是依賴於SpringMVC框架。在實現上基於Java的反射機制,屬於面向切面編程(AOP)的一種運用。因爲攔截器是基於web框架的調用,所以可使用Spring的依賴注入(DI)進行一些業務操做,同時一個攔截器實例在一個controller生命週期以內能夠屢次調用。可是缺點是隻能對controller請求進行攔截,對其餘的一些好比直接訪問靜態資源的請求則沒辦法進行攔截處理
3.過濾器和攔截器的區別:
①攔截器是基於java的反射機制的,而過濾器是基於函數回調。
②攔截器不依賴與servlet容器,過濾器依賴與servlet容器。
③攔截器只能對action請求起做用,而過濾器則能夠對幾乎全部的請求起做用。
④攔截器能夠訪問action上下文、值棧裏的對象,而過濾器不能訪問。
⑤在action的生命週期中,攔截器能夠屢次被調用,而過濾器只能在容器初始化時被調用一次。
⑥攔截器能夠獲取IOC容器中的各個bean,而過濾器就不行,這點很重要,在攔截器裏注入一個service,能夠調用業務邏輯。
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException { System.out.println("before..."); chain.doFilter(request, response); System.out.println("after..."); }
chain.doFilter(request, response);這個方法的調用做爲分水嶺。事實上調用Servlet的doService()方法是在chain.doFilter(request, response);這個方法中進行的。
**