SpringMVC攔截器Interceptor


SpringMVC攔截器(Interceptor)實現對每一個請求處理前后進行相關的業務處理,類似與servlet中的Filter。

SpringMVC 中的Interceptor 攔截請求是通過HandlerInterceptor來實現的。

在SpringMVC中定義一個Interceptor非常簡單,主要有4種方式:

1)實現Spring的HandlerInterceptor接口;

2)繼承實現了HandlerInterceptor接口的類,比如Spring 已經提供的實現了HandlerInterceptor 接口的抽象類HandlerInterceptorAdapter;

3)實現Spring的WebRequestInterceptor接口;

4)繼承實現了WebRequestInterceptor的類;

實現了攔截器之后,我們可以通過重寫WebMvcConfigurerAdapter的addInterceptors方法來注冊自己的攔截器。

 

我們這里只通過實現HandlerInterceptor接口的方式給出實例。實例中使用攔截器實現兩個功能

1)計算每一次請求的處理時間

2)並對特定時間和特定用戶(數據在codis中)的請求進行拒絕

 

 1、HandlerInterceptor接口

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;
}
  • preHandle():預處理回調方法,若方法返回值為true,請求繼續(調用下一個攔截器或處理器方法);若方法返回值為false,請求處理流程中斷,不會繼續調用其他的攔截器或處理器方法,此時需要通過response產生響應;
  • postHandle():后處理回調方法,實現處理器的后處理(但在渲染視圖之前),此時可以通過modelAndView對模型數據進行處理或對視圖進行處理
  • afterCompletion():整個請求處理完畢回調方法,即在視圖渲染完畢時調用

HandlerInterceptor有三個方法需要實現,但大部分時候可能只需要實現其中的一個方法,HandlerInterceptorAdapter是一個實現了HandlerInterceptor的抽象類,它的三個實現方法都為空實現(或者返回true),繼承該抽象類后可以僅僅實現其中的一個方法。

 

2、實現攔截器

 

package com.xiaoju.dqa.sentinel.monitor.interceptor;


import com.xiaoju.dqa.sentinel.common.client.redis.CodisClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Calendar;
import java.util.Set;


public class AuthInterceptor implements HandlerInterceptor {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private CodisClient codisClient;

    /*
    *   視圖函數執行成功后執行
    * */
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
    }

    /*
    *   在視圖函數之后執行
    *   本函數的作用:計算處理時間
    * */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {
        long startTime = (Long) request.getAttribute("startTime");
        request.removeAttribute("startTime");
        logger.info("處理時間: {}", System.currentTimeMillis() - startTime);
    }

    /*
    *   在視圖函數之前執行
    *   返回true, 繼續執行視圖函數
    *   返回false, 終止請求流程
    *   本函數的作用,:拒絕特定時間sentinel:forbidden:hours; 特定用戶的sentinel:forbidden:users請求, 並記錄startTime
    * */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        boolean going = true;
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        Calendar ca = Calendar.getInstance();
        String  currentHour = String.valueOf(ca.get(Calendar.HOUR_OF_DAY));
        try {
            boolean isForbidHour = codisClient.sismember("sentinel:forbidden:hours", currentHour);
            if (isForbidHour) {
                Set<String> forbiddenUsers = codisClient.smembers("sentinel:forbidden:users");
                if (forbiddenUsers != null) {
                    for (Cookie cookie : request.getCookies()) {
                        if("username".equals(cookie.getName()) && forbiddenUsers.contains(cookie.getValue())) {
                            logger.info("[攔截器] 禁止訪問. 時間:{}, 用戶:{}", currentHour, cookie.getValue());
                            going = false;
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.info("[攔截器] 有問題", e);
        }
        return going;
    }
}

  

  

 

3、注冊攔截器

 這里預先生成了@bean - authInterceptor是為了讓AuthInterceptor類中的codisClient的注入成功,否則即使自動注入了codisClient也無法注入成功。

package com.xiaoju.dqa.sentinel.configuration;

import com.xiaoju.dqa.sentinel.monitor.interceptor.AuthInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;


@Configuration
public class WebInterceptorConfig extends WebMvcConfigurerAdapter {

    @Bean
    public AuthInterceptor authInterceptor() {
        return new AuthInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 多個攔截器組成一個攔截器鏈
        // addPathPatterns 用於添加攔截規則
        // excludePathPatterns 用戶排除攔截
        registry.addInterceptor(authInterceptor()).addPathPatterns("/**").excludePathPatterns("/sentinel/monitor/**");
        super.addInterceptors(registry);
    }

}

  

 

 

 


免責聲明!

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



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