过滤器和拦截器
过滤器(Filter)
属于Servlet(Server Applet)中的技术,可以通过Filter去管理处理web资源,可以对指定的一些行为进行拦截,进行例如权限控制、访问控制等。
在ServletRequest到达Servlet之前,拦截客户端的ServletRequest,可以根据需要检查ServletRequest,也可以修改ServletRequest中的头和数据
在ServletResponse到达客户端之前,可以拦截ServletResponse,可以根据需要检查ServletResponse,同样也可以修改。
通过实现Filter接口(属于javax.servlet包),实现doFilter()方法,即可自定义过滤器。
拦截器(Interceptor)
Interceptor拦截器是Spring MVC框架中对请求进行拦截和处理的组件,可以实现权限验证、日志记录、异常处理等功能,拦截器是在Spring MVC框架中执行的。
在HttpServletRequest到达Controller之前,可以根据需要检查HttpServletRequest,也可以进行修改。
在HttpServletResponse返回之前,可以根据需要检查HttpServletResponse,也可以进行修改。
通过实现HandlerInterceptor接口(属于org.springframework.web.servlet包),实现preHandle(前置)、postHandle(后置)、afterCompletion(完成后)方法,即可自定义拦截器。
出身
过滤器来自Servlet、拦截器来自Spring
触发时机
请求的执行顺序为:
请求进入容器 > 进入过滤器 > 进入Servlet > 进入拦截器 > 进入控制器(Controller)
实现
- 过滤器是基于方法回调实现
- 拦截器是基于动态代理(底层反射)实现的
当我们执行下一个过滤器或下一个流程时,就要调用FilterChain对向的doFilter方法进行回调执行:
拦截器的实现方式:
支持的项目类型
过滤器是在Servlet中定义的,所以过滤器要依赖Servlet容器,所以他只能用在Web项目中
拦截器是Spring中定义的,属于Spring中的一个组件,因此它既可以用在Web项目中,又可以用在Application或Swing程序中
使用场景
过滤器通常用来实现比较【通用功能】的过滤,比如:敏感词过滤、字符集编码设置、响应数据的处理等等
拦截器因为更接近业务层面,所以主要用来实现项目中的对业务的判断,比如:登陆校验、权限控制、日志记录等等
实现方式
过滤器
实现
可以使用 Servlet 3.0 提供的 @WebFilter 注解,配置过滤的 URL 规则,然后再实现 Filter 接口,重写接口中的 doFilter 方法,具体实现代码如下:
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@Component
@WebFilter(urlPatterns = "/*")
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器:执行 init 方法。");
}
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器:开始执行 doFilter 方法。");
// 请求放行
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("过滤器:结束执行 doFilter 方法。");
}
@Override
public void destroy() {
System.out.println("过滤器:执行 destroy 方法。");
}
}
void init(FilterConfig filterConfig):容器启动(初始化 Filter)时会被调用,整个程序运行期只会被调用一次。用于实现 Filter 对象的初始化。
void doFilter(ServletRequest request, ServletResponse response,FilterChain chain):具体的过滤功能实现代码,通过此方法对请求进行过滤处理,其中 FilterChain 参数是用来调用下一个过滤器或执行下一个流程。
void destroy():用于 Filter 销毁前完成相关资源的回收工作。
注册方式
三种注册方式
xml配置(太原始就略过)
@Component
配置类
@Component
使用@Component直接加在过滤器类上面、spring自动检测后通过组件扫描将其注册,这种方式简单易用,适合小型应用和快速开发的场景
配置类
如果想可以更加灵活控制过滤器,可以使用配置类的方式
通过继承WebFilterConfigurerAdapter或直接注入FilterRegistrationBean来完成注册
方式一:基于FilterRegistrationBean
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfiguration {
@Bean
public FilterRegistrationBean<MyCustomFilter> loggingFilter(){
FilterRegistrationBean<MyCustomFilter> registrationBean = new FilterRegistrationBean<>();
MyCustomFilter myCustomFilter = new MyCustomFilter();
registrationBean.setFilter(myCustomFilter);
// 设置URL模式匹配规则
registrationBean.addUrlPatterns("/api/*");
// 定义加载顺序
registrationBean.setOrder(1);
return registrationBean;
}
}
方式二:重写 WEBMVCCONFIGURER 的 ADDFILTERS 方法
如果需要进一步扩展MVC的功能,可以考虑覆盖默认配置以及添加额外的功能支持
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CustomWebConfig implements WebMvcConfigurer {
private final MyCustomFilter customFilter;
@Autowired
public CustomWebConfig(MyCustomFilter customFilter){
this.customFilter = customFilter;
}
@Override
public void addFilters(javax.servlet.FilterRegistration.Dynamic registration,
String[] urlPatterns) {
super.addFilters(registration,urlPatterns);
registration.addMappingForUrlPatterns(null,true,"/custom/**");
}
}
过滤器Filter可以拿到原始的HTTP请求和响应的信息,但是拿不到你真正处理请求方法的信息
在 Java Web 开发中,过滤器(Filter)能够获取到客户端发送过来的原始 HTTP 请求(比如请求头、请求参数等)以及服务器准备返回给客户端的响应信息(比如响应状态码、响应头 等)。但它默认情况下不能直接获取到在后端具体处理该请求的业务方法(比如在 Controller 类中的某个处理请求的方法)的相关信息,像方法名、方法参数等。
拦截器
拦截器的实现基本就分为两步:
创建一个拦截器,实现HandlerInterceptor接口,并重写接口中的相关方法
将创建的拦截器加入到Springboot的配置文件中
创建拦截器
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 TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器:执行 preHandle 方法。");
return true;
}
@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 方法。");
}
}
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handle):在请求方法执行前被调用,也就是调用目标方法之前被调用。比如我们在进入接口之前先要验证用户的登录信息,就可以在此方法中实现,如果验证成功则返回 true,继续执行数据操作业务;否则就返回 false,后续操作数据的业务就不会被执行了。
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView):调用请求方法之后执行,但它会在 DispatcherServlet 进行渲染视图之前被执行。
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex):会在整个请求结束之后再执行,也就是在 DispatcherServlet 渲染了对应的视图之后再执行
加入配置
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 AppConfig implements WebMvcConfigurer {
// 注入拦截器
@Autowired
private TestInterceptor testInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(testInterceptor) // 添加拦截器
.addPathPatterns("/*"); // 拦截所有地址
}
}