Springboot中SpringMvc攔截器配置與應用(實戰)


一、什么是攔截器,及其作用

攔截器(Interceptor): 用於在某個方法被訪問之前進行攔截,然后在方法執行之前或之后加入某些操作,其實就是AOP的一種實現策略。它通過動態攔截Action調用的對象,允許開發者定義在一個action執行的前后執行的代碼,也可以在一個action執行前阻止其執行。同時也是提供了一種可以提取action中可重用的部分的方式。

攔截器的使用場景越來越多,尤其是面向切片編程流行之后。那通常攔截器可以做什么呢?
之前我們在Agent介紹中,提到過統計函數的調用耗時。這個思路其實和AOP的環繞增強如出一轍。

那一般來說,場景如下:

1、日志記錄:記錄請求信息的日志,以便進行信息監控、信息統計、計算PV(Page View)等。

2、權限檢查:如登錄檢測,進入處理器檢測檢測是否登錄,如果沒有直接返回到登錄頁面;

3、函數增強:比如對一個函數進行參數檢查,或者結果過濾等。甚至可以對函數就行權限認證。

4、性能監控:統計函數性能,有時候系統在某段時間莫名其妙的慢,可以通過攔截器在進入處理器之前記錄開始時間,在處理完后記錄結束時間,從而得到該請求的處理時間(如果有反向代理,如apache可以自動記錄);

5、通用行為:讀取cookie得到用戶信息並將用戶對象放入請求,從而方便后續流程使用,還有如提取Locale、Theme信息等,只要是多個處理器都需要的即可使用攔截器實現。

6、OpenSessionInView:如Hibernate,在進入處理器打開Session,在完成后關閉Session。

…………本質也是AOP(面向切面編程),也就是說符合橫切關注點的所有功能都可以放入攔截器實現。

二、springmvc攔截器相關接口和實現類

package org.springframework.web.servlet; public interface HandlerInterceptor { boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception; } 
public interface AsyncHandlerInterceptor extends HandlerInterceptor { void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; }

public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor { // 在目標方法執行前執行
 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return true; } // 在目標方法執行后執行,但在請求返回前,我們仍然可以對 ModelAndView進行修改
 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {} // 在請求已經返回之后執行
 @Override public void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {} // 用來處理異步請求, 當Controller中有異步請求方法的時候會觸發該方法
 @Override public void afterConcurrentHandlingStarted( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {} }

 

三、如何配置攔截器

1、springboot項目中如何配置攔截器

實現自定義攔截器只需要3步: 
1、創建我們自己的攔截器類並實現 HandlerInterceptor 接口。 
2、創建一個Java類繼承WebMvcConfigurerAdapter,並重寫 addInterceptors 方法。 
3、實例化我們自定義的攔截器,然后將對像手動添加到攔截器鏈中(在addInterceptors方法中添加)。

package com.springboot.study.interceptors;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
public class MyInterceptor1 implements HandlerInterceptor{
 
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        System.out.println("=====>(1)在整個請求之后調用,即在dispatcherServlet渲染了對應的視圖之后(主要是進行資源清理工作)");
    }
 
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {
        System.out.println("=====>(1)在請求處理之后調用,即在controller方法執行之后調用");
    }
 
    @Override
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
        System.out.println("=====>(1)在請求處理之前調用,即在Controller方法調用之前!");
        return true;//只有返回true才會往下執行,返回FALSE的話就會取消當前請求
    }
 
}
package com.springboot.study.interceptors;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
public class MyInterceptor2 implements HandlerInterceptor{
 
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        System.out.println("=====>(2)在整個請求之后調用,即在dispatcherServlet渲染了對應的視圖之后(主要是進行資源清理工作)");
    }
 
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {
        System.out.println("=====>(2)在請求處理之后調用,即在controller方法執行之后調用");
    }
 
    @Override
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
        System.out.println("=====>(2)在請求處理之前調用,即在Controller方法調用之前!");
        return true;//只有返回true才會往下執行,返回FALSE的話就會取消當前請求
    }
 
}
package com.springboot.study.controller;
 
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
public class MyController {
    
    @RequestMapping("/index")
    public String index(){
        return "hello!";
    }
    
}
package com.springboot.study.config;
 
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
 
import com.springboot.study.interceptors.MyInterceptor1;
import com.springboot.study.interceptors.MyInterceptor2;
 
@Configuration
public class MyWebAppConfigurer extends WebMvcConfigurerAdapter{
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
        registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }
}
運行結果:
=====>(1)在請求處理之前調用,即在Controller方法調用之前! =====>(2)在請求處理之前調用,即在Controller方法調用之前! =====>(2)在請求處理之后調用,即在controller方法執行之后調用 =====>(1)在請求處理之后調用,即在controller方法執行之后調用 =====>(2)在整個請求之后調用,即在dispatcherServlet渲染了對應的視圖之后(主要是進行資源清理工作) =====>(1)在整個請求之后調用,即在dispatcherServlet渲染了對應的視圖之后(主要是進行資源清理工作)

 

四、實例

自定義權限注解

定義一個@interface

@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Access { String[] value() default {}; String[] authorities() default {}; String[] roles() default {}; }

@Target注解是標注這個類它可以標注的位置:
常用的元素類型(ElementType):

public enum ElementType { /** Class, interface (including annotation type), or enum declaration */
    // TYPE類型可以聲明在類上或枚舉上或者是注解上
 TYPE, /** Field declaration (includes enum constants) */
    // FIELD聲明在字段上
 FIELD, /** Method declaration */
    // 聲明在方法上
 METHOD, /** Formal parameter declaration */
    // 聲明在形參列表中
 PARAMETER, /** Constructor declaration */
    // 聲明在構造方法上
 CONSTRUCTOR, /** Local variable declaration */
    // 聲明在局部變量上
 LOCAL_VARIABLE, /** Annotation type declaration */ ANNOTATION_TYPE, /** Package declaration */ PACKAGE, /** * Type parameter declaration * * @since 1.8 */ TYPE_PARAMETER, /** * Use of a type * * @since 1.8 */ TYPE_USE }

@Retention注解表示的是本注解(標注這個注解的注解保留時期)

public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. */
    // 源代碼時期
 SOURCE, /** * Annotations are to be recorded in the class file by the compiler * but need not be retained by the VM at run time. This is the default * behavior. */
    // 字節碼時期, 編譯之后
 CLASS, /** * Annotations are to be recorded in the class file by the compiler and * retained by the VM at run time, so they may be read reflectively. * * @see java.lang.reflect.AnnotatedElement */
     // 運行時期, 也就是一直保留, 通常也都用這個
 RUNTIME }

@Documented是否生成文檔的標注, 也就是生成接口文檔是, 是否生成注解文檔

注解說完了, 下面需要到對應的controller的方法中取添加注解, 配置該方法允許的權限

在方法上配置權限

@RestController public class HelloController { @RequestMapping(value = "/admin", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.GET) // 配置注解權限, 允許身份為admin, 或者說允許權限為admin的人訪問
    @Access(authorities = {"admin"}) public String hello() { return "Hello, admin"; } }

編寫權限攔截邏輯

// 自定義一個權限攔截器, 繼承HandlerInterceptorAdapter類
public class AuthenticationInterceptor extends HandlerInterceptorAdapter { // 在調用方法之前執行攔截
 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 將handler強轉為HandlerMethod, 前面已經證實這個handler就是HandlerMethod
        HandlerMethod handlerMethod = (HandlerMethod) handler; // 從方法處理器中獲取出要調用的方法
        Method method = handlerMethod.getMethod(); // 獲取出方法上的Access注解
        Access access = method.getAnnotation(Access.class); if (access == null) { // 如果注解為null, 說明不需要攔截, 直接放過
            return true; } if (access.authorities().length > 0) { // 如果權限配置不為空, 則取出配置值
            String[] authorities = access.authorities(); Set<String> authSet = new HashSet<>(); for (String authority : authorities) { // 將權限加入一個set集合中
 authSet.add(authority); } // 這里我為了方便是直接參數傳入權限, 在實際操作中應該是從參數中獲取用戶Id // 到數據庫權限表中查詢用戶擁有的權限集合, 與set集合中的權限進行對比完成權限校驗
            String role = request.getParameter("role"); if (StringUtils.isNotBlank(role)) { if (authSet.contains(role)) { // 校驗通過返回true, 否則攔截請求
                    return true; } } } // 攔截之后應該返回公共結果, 這里沒做處理
        return false; } }

 

攔截器詳解源碼地址:https://www.cnblogs.com/fangjian0423/p/springMVC-interceptor.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM