拦截器、过滤器区别
出身不同
拦截器实现的是HandlerInterceptor
接口,拦截器是属于Spring
技术,它是Spring
的一个组件,并由Spring
容器创建管理,并不依赖Tomcat
服务器,是可以单独使用的,拦截器不仅能应用在web
程序中,也可以用于Application
、Swing
等程序中;
过滤器实现是javax.servlet.Filter
接口,而这个接口是在Servlet
规范中定义的,也就是说过滤器Filter
的使用要依赖于Tomcat
服务器,Filter
是在Tomcat
服务器创建的对象,导致它只能在web程序中使用。
使用场景不同
拦截器侧重于验证请求、截断请求,对用户请求做预先的判断处理、可以修改ModelAndView
对象中的数据和视图,影响控制器方法最终的执行结果的,拦截器主要应用于登陆校验、日志记录、权限验证、性能监控等功能:
过滤器侧重于对请求参数进行过滤的, 比如敏感词过滤、字符集编码设置CharacterEncodingFilter
、修改请求头和响应头的信息;
触发时机不同
拦截器Interceptor
是在请求进入servlet
后,进入Controller
之前进行预处理的,Controller
中渲染了对应的视图之后请求结束。
过滤器Filter
是在请求进入容器后,但在进入servlet
之前进行预处理,请求结束是在servlet
处理完以后。
过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller
中请求或访问static
目录下的资源请求起作用。
实现不同
拦截器是基于动态代理(底层是反射)实现的。
过滤器是基于方法回调实现的,当我们要执行下一个过滤器或下一个流程时,需要调用FilterChain
对象的doFilter
方法进行回调执行。
部分应用场景示例
拦截器:
1、登录验证,判断用户是否登录;
2、权限验证,判断用户是否有权限访问资源,如校验token;
3、日志记录,记录请求操作日志(用户ip,访问时间等),以便统计请求访问量;
4、处理 cookie、本地化、国际化、主题等;
5、性能监控,监控请求处理时长等;
6、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的即可使用拦截器实现;
过滤器
1、过滤敏感词汇(防止sql注入);
2、设置字符编码;
3、URL级别的权限访问控制;
4、压缩响应信息;
拦截器的使用
新建拦截器
java
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class ServletInterceptor implements HandlerInterceptor {
/**
* 在请求处理之前进行调用(Controller方法调用之前)
* 预处理回调方法,实现处理器的预处理
* 返回值:true表示继续流程;false表示流程中断,不会继续调用其他的拦截器或处理器
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取请求头中的token
String token = request.getHeader("token");
//如果请求头中没有token,抛出异常
if (StrUtil.isBlankIfStr(token)) {
// 返回false 或者 自定义封装的异常
throw new MySelfException("请求头缺少token");
}
return true;
}
/**
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
* 后处理回调方法,实现处理器(controller)的后处理,但在渲染视图之前
* 此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
* 整个请求处理完毕回调方法,即在视图渲染完毕时回调,
* 如性能监控中我们可以在此记录结束时间并输出消耗时间,
* 还可以进行一些资源清理,类似于try-catch-finally中的finally,
* 但仅调用处理器执行链中
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
配置拦截器
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private ServletInterceptor servletInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(servletInterceptor)
// 配置拦截的路径
.addPathPatterns("/**")
// 配置不需拦截的路径
.excludePathPatterns("/admin/**")
// 配置拦截器顺序,数字越大执行越靠后
.order(1);
}
}
过滤器的使用
新建过滤器
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
@Slf4j
public class ServletStreamFilter implements Filter {
@Override //可不重写
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
log.info("ServletStreamFilter 过滤器成功初始化");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
String contentType = req.getContentType();
String method = "multipart/form-data";
if (contentType != null && contentType.contains(method)) {
// 将转化后的 request 放入过滤链中
request = new StandardServletMultipartResolver().resolveMultipart(request);
}
// 若后续不在使用request中的东西,直接放行即可
// chain.doFilter(req, resp);
// 若后续还会用到request中的东西,使用该方法操作
// 扩展request,使其能够能够重复读取requestBody
ServletRequest requestWrapper = new RequestWrapper(request);
// 这里需要放行,但是要注意放行的 request是requestWrapper
chain.doFilter(requestWrapper, resp);
}
@Override //可不重写
public void destroy() {
Filter.super.destroy();
log.info("ServletStreamFilter 过滤器被销毁");
}
// 内部类
static class RequestWrapper extends HttpServletRequestWrapper {
/**
* 存储body数据的容器
*/
private final byte[] body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
// 将body数据存储起来
String bodyStr = getBodyString(request);
body = bodyStr.getBytes(Charset.defaultCharset());
}
/**
* 获取请求Body
*
* @param request request
* @return String
*/
public String getBodyString(final ServletRequest request) throws IOException {
return inputStream2String(request.getInputStream());
}
/**
* 获取请求Body
*
* @return String
*/
public String getBodyString() throws IOException {
final InputStream inputStream = new ByteArrayInputStream(body);
return inputStream2String(inputStream);
}
/**
* 将inputStream里的数据读取出来并转换成字符串
*
* @param inputStream inputStream
* @return String
*/
private String inputStream2String(InputStream inputStream) throws IOException {
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
}
配置过滤器
java
import com.company.filter.ServletStreamFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyFilterConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean(){
// 创建一个注册过滤器对象
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new ServletStreamFilter());
// 设置过滤拦截匹配规则,/*是匹配所有
// registrationBean.addUrlPatterns("/*");
// 只拦截test下面的接口
registrationBean.addUrlPatterns("/test/*");
// 给过滤器起名字
registrationBean.setName("servletStreamFilter-");
// 存在多个过滤器时,设置执行顺序,值越大,执行顺序越靠后
registrationBean.setOrder(2);
// 返回这个注册过滤器对象
return registrationBean;
}
}