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