首先说一下为什么引出这三种拦截机制
在某些应用场景下,对于一个用户而言,我们需要在他访问接口的时候去进行一系列的校验(身份、权限、系统关系等),同时也为了防止恶意攻击,出于系统内部的安全性考虑,有必要去进行加入拦截机制
三种拦截机制:包括最原始的filter、interceptor、aspect
一、Filter
创建filter类来实现Filter(servlet包下)类,实现init、doFilter和destory方法,在doFilter方法中去拦截请求,要想使Filter起作用:可以在过滤器类上添加注解@Component去拦截所有的请求,还可以通过配置去给特定的url去起作用,如下:
但是使用Fiter并不能知道哪个请求是用哪个过滤器的什么方法去处理的,因为所有的请求都是基于spring的。
二、interceptor
是Spring框架本身提供的。拦截器会拦截所有控制器
@Component
public class TimeInterceptor implements HandlerInterceptor {
/**
* 在控制器的方法调用之前调用
* @param request
* @param response
* @param handler 比filter多的一个参数,真正用来处理逻辑的一个参数
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
//打印类名和方法名
System.out.println(((HandlerMethod)handler).getBean().getClass().getName());
System.out.println(((HandlerMethod)handler).getMethod().getName());
request.setAttribute("startTime",new Date().getTime());
return true;
}
/**
* 在控制器的方法调用之后调用,但是抛出异常之后不会进入这个方法
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
Long start = (Long) request.getAttribute("startTime");
// request.setAttribute("endTime",new Date().getTime()-);
System.out.println("postHandle请求耗时:"+ (new Date().getTime() - start));
}
/**
* 不管是正常调用还是抛出异常都会被调用
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
Long start = (Long) request.getAttribute("startTime");
// request.setAttribute("endTime",new Date().getTime()-);
System.out.println("afterCompletion请求耗时:"+ (new Date().getTime() - start));
System.out.println("ex is " + ex);
}
}
写完之后并不会生效,还需要去对其进行注册,注册的代码:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private TimeInterceptor timeInterceptor;
//拦截器的注册器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor);
}
}
这样,拦截器就会拦截所有的请求。
注意:在拦截器中,handler是用来过滤的方法,但是这个方法只能获取到此次请求所用到的那个类和那个方法,并不能看到传递的参数以及传递的对象。追踪源码:在DispatcherServlet中的doService方法里面调用了doDispatcher()方法,在doDispatcher()方法中有一个appplyPreHandle方法,这个方法就是去调用我们写的那个拦截器的preHandle方法,只有在preHandle方法返回true的时候才往下执行,下面会调用handle方法,在这个方法中会对请求中传递的参数进行拼装,将传递的JSON字符串等数据组装成我们需要的一些自定义对象。所以,采用拦截器的方法虽然能知道此次请求调用的类和方法,但是并不能知道请求中传递的参数,如果需要知道传递的参数,则需要使用切片(Aspect)的方式。
三、Aspect
这种方式主要使用了Spring的AOP的思想
1.切入点
四个注解用来控制什么时候起作用:
- @before 调用方法之前
- @after 调用方法之后
- @afterThrowing 调用方法之后,如果指定的方法抛出异常,则执行这个
- @around 完全覆盖了之前的三种,都可以用这种方式
2.Aspect实现
其中,注解中的表达式可以参考面向切面编程表达式的写法
我们推荐使用这个去进行拦截,但是缺点是拿不到原始的http请求和响应的那两个对象