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