过滤器
什么是过滤器(Filter)
过滤器是基于servlet实现,过滤器的主要应用场景是对字符编码、跨域等问题进行过滤。Servlet的工作原理是拦截配置好的客户端请求,然后对Request和Response进行处理。Filter过滤器随着web应用的启动而启动,只初始化一次。
Filter的使用比较简单,继承Filter接口,实现对应的init、doFilter以及destroy方法即可。
- init:在容器启动时调用初始化方法,只会初始化一次
- doFilter:每次请求都会调用doFilter方法,通过FilterChain 调用后续的方法
- destroy:当容器销毁时,执行destory方法,只会被调用一次。
使用方法
- 实现 Filter 接口,重写 doFilter 方法;
- 放行请求时调用chain.doFilter()方法;
- 启用该过滤器,有三种方式(第二种方式使用的较多)
- 一种是比较原始的xml配置,暂时不重点研究。
- 第二种是使用注解 @WebFilter() ,并在启动类上添加@ServletComponentScan注解使用。
- 第三种是直接使用@Component注解,这样的话@WebFilter配置的路径会失效,因为@WebFilter根本就没生效,不信自己去试试
- 设置拦截路径,就是要拦截的那个url路径。
代码实现
1.基于第一种方式的变形:不使用@WebFilter(),即需要编写过滤器类,又需要编写配置类
- 过滤器类
过滤器类就是指 当拦截到某一个请求后,在该请求发往servlet容器前,执行什么操作,需要进行什么处理
public class LogCostFilter implements Filter{
@Override
public void init(FilterConfig filterConfig)throws ServletException{
}
@Override
public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain)throws IOException,ServletException{
long start =System.currentTimeMillis();
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("Execute cost="+(System.currentTimeMillis()-start));
}
@Override
public void destroy(){
}
}
这段代码的逻辑比较简单,就是在方法执行前先记录时间戳,然后通过过滤器链完成请求的执行,在返回结果之间计算执行的时间。这里需要注意,这个类必须继承Filter类,这个是Servlet的规范,然后重写init、doFilter、destroy方法
- 配置类
配置类中配置过滤器需要拦截的请求类型的正则表达式以及使用那个过滤器
有了过滤器类以后,以前的web项目可以在web.xml中进行配置,但是spring- boot项目并没有web.xml这个文件,那怎么配置?在Spring boot中,我们需要FilterRegistrationBean来完成配置。其实现过程如下
@Configuration
public class FilterConfig{
@Bean
public FilterRegistrationBean registFilter(){
FilterRegistrationBean registration =new FilterRegistrationBean();
registration.setFilter(newLogCostFilter());
registration.addUrlPatterns("/*");
registration.setName("LogCostFilter");
registration.setOrder(1);
return registration;
}
}
这样配置就完成了,需要配置的选项主要包括实例化Filter类,然后指定url的匹配模式,设置过滤器名称和执行顺序,这个过程和在web.xml中配置其实没什么区别,只是形式不同而已。
2.基于第二种方式:使用@WebFilter和过滤器类,不需要编写配置类
@WebFilter(urlPatterns ="/*", filterName ="logFilter2")
public class LogCostFilter2 implements Filter{
@Override
publicvoid init(FilterConfig filterConfig)throwsServletException{
}
@Override
public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain)throwsIOException,ServletException{
long start =System.currentTimeMillis();
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("LogFilter2 Execute cost="+(System.currentTimeMillis()- start));
}
@Override
public void destroy(){
}
}
这里直接用@WebFilter就可以进行配置,同样,可以设置url匹配模式,过滤器名称等。这里需要注意一点的是@WebFilter这个注解是Servlet3.0的规范,并不是Spring boot提供的。除了这个注解以外,我们还需在配置类中加另外一个注解:@ServletComponetScan,指定扫描的包。
@SpringBootApplication
@MapperScan("com.pandy.blog.dao")
@ServletComponentScan("com.pandy.blog.filters")
public class springbootApplication{
public static void main(String[] args) throws Exception{
SpringApplication.run(Application.class, args);
}
}
拦截器
什么是拦截器()
拦截器是SpringMVC中实现的一种基于Java反射(动态代理)机制的方法增强工具,拦截器的实现是继承HandlerInterceptor 接口,并实现接口的preHandle、postHandle和afterCompletion方法。
- preHandle:请求方法前置拦截,该方法会在Controller处理之前进行调用,Spring中可以有多个Interceptor,这些拦截器会按照设定的Order顺序调用,当有一个拦截器在preHandle中返回false的时候,请求就会终止。
- postHandle:preHandle返回结果为true时,在Controller方法执行之后,视图渲染之前被调用。
- afterCompletion:在preHandle返回ture,并且整个请求结束之后,执行该方法。
使用方法
- 实现 HandlerInterceptor 接口 或 继承 HandlerInterceptorAdapter 类,建议使用接口;
- 实现 preHandle 方法(在处理请求前运行),实现 postHandle 方法(在处理请求完毕后运行);
- 再新建一个类,继承 WebMvcConfigurer 接口,再实现addInterceptors方法,并在方法中注册该拦截器、配置拦截路径(不配置默认拦截所有请求);
代码实现
- 拦截器
@Component
public class UserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
String userName=request.getParameter("userName");
String password = request.getParameter("password");
if (userName==null||password==null){
response.setStatus(500);
response.setContentType("text/html; charset=UTF-8");
response.getWriter().print("参数缺失");
return false;
}
//进行用户校验
if (userName.equals("admin")&&password.equals("admin")){
return true;
}else {
response.setStatus(500);
response.setContentType("text/html; charset=UTF-8");
response.getWriter().print("用户名或密码错误");
return false;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
- 配置类
编写完拦截器之后,通过一个配置类设置拦截器,并且可以通过**addPathPatterns(拦截)和excludePathPatterns(不拦截)**执行哪些请求需要被拦截,哪些不需要被拦截。
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Autowired
private UserInterceptor userInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/error");
}
}
过滤器及拦截器的区别
tomcat,filter,servet,interceptor以及controller等各种容器的关系图
过滤器及拦截器的执行顺序
首先当一个请求进入Servlet之前,过滤器的doFilter方法进行过滤,
进入Servlet容器之后,执行Controller方法之前,拦截器的preHandle方法进行拦截,
执行Controller方法之后,视图渲染之前,拦截器的postHandle方法进行拦截,
请求结束之后,执行拦截器的postHandle方法。
区别
- 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
- 拦截器不依赖于servlet容器依赖于spring容器,过滤器依赖于servlet容器。
- 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用,可以限制用户对图片,文件以及其他资源的访问。
- 拦截器可以访问action请求上下文、值栈里的对象,而过滤器不能访问。
- 拦截器可以多次被调用,而过滤器只需在容器初始化时被调用一次,会一直在驻留在内存中,对匹配到到请求做过滤
代码审计过程中需要关注的函数
过滤器(Filter) | Filter | @WebFilter | doFilter | @ServletComponentScan | FilterRegistrationBean |
---|---|---|---|---|---|
拦截器(Intercepter) | HandlerIntercepor | WebMvcConfigurer | addInterceptors | addPathPartterns | excludePathPartterns |