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); } }