一、什么是过滤器,过滤器有什么用?
过滤器(Filter)是一种在数据处理流程中用于拦截、检查、修改和处理数据的机制或组件。它可以实现对数据的预处理。
JavaWeb中的过滤器:在Java Web开发中,过滤器是一种运行在客户端请求和服务器响应之间的代码,用于检查和处理这些请求和响应。例如,过滤器可以用于执行安全检查、日志记录、请求数据的修改、响应内容的修改等。JavaWeb中的过滤器通常通过实现javax.servlet.Filter
接口来创建,并可以在web.xml
文件中配置或通过注解指定其在过滤链中的顺序。
二、Filter官方介绍
话不多说,直接上源码兄弟们!注释我给兄弟们翻译好了,下一节有过滤器的使用方法
/**
过滤器是一个对象,它对对资源(servlet或静态内容)的请求或对来自资源的响应执行过滤任务,或者对两者都执行过滤任务。
过滤器在doFilter方法中执行过滤。每个Filter都可以访问FilterConfig对象,从中获取初始化参数,以及对ServletContext的引用,例如,它可以使用ServletContext来加载过滤任务所需的资源。
过滤器在web应用程序的部署描述符中配置。已确定用于此设计的示例有:
1.身份验证过滤器
2.日志和审计过滤器
3.图像转换过滤器
4.数据压缩过滤器
5.加密的过滤器
6.Token过滤器
7.触发资源访问事件的过滤器
8.XSL/T过滤器
9.Mime-type 链过滤器
*/
public interface Filter {
/**
这个方法在过滤器实例化并且放入服务之前被Web容器调用一次。FilterConfig对象包含过滤器的配置和初始化参数。如果这个方法抛出ServletException,或者没有在Web容器定义的时间段内返回,Web容器不会将这个过滤器放入服务。
*/
default public void init(FilterConfig filterConfig) throws ServletException {
}
/**
当请求/响应对由于客户端对资源的请求而通过链传递时,这个方法被容器调用。FilterChain对象允许过滤器将请求和响应传递给链中的下一个实体。这个方法的典型实现包括检查请求、选择性地对请求对象进行包装以过滤输入内容或头部、选择性地对响应对象进行包装以过滤输出内容或头部,然后要么调用链中的下一个实体,要么不传递请求/响应对以阻止请求处理。
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
/**
当Web容器指示过滤器被取出服务时调用这个方法。只有在过滤器的doFilter方法中的所有线程都已退出或者超时期已经过去之后,这个方法才会被调用。调用这个方法之后,Web容器不会再次在这个过滤器实例上调用doFilter方法。这个方法给过滤器一个机会来清理任何正在持有的资源(例如,内存、文件句柄、线程)并确保任何持久状态与内存中的过滤器当前状态同步。
*/
default public void destroy() {
}
}
三、Filter的基本使用
就举一个Filter最常用的场景吧
身份验证过滤器
身份验证过滤器(Authentication Filter)是Web应用中一种常见的安全机制,用于确保只有经过授权的用户才能访问特定的资源。这种类型的过滤器在用户请求访问资源之前拦截请求,并检查用户是否已经通过身份验证(即是否已登录),如果未通过,则可能重定向用户到登录页面,或返回一个指示需要身份验证的响应。下面是一个简单的身份验证过滤器的例子:
示例场景
假设你正在开发一个需要用户登录后才能访问的Web应用。你需要一个身份验证过滤器来保护那些只有登录用户才能访问的资源。
1)自定义一个过滤器实现Filter接口,在doFilter中获取请求头中的Authorization(token),解析判断token是否合法再决定是否放行,当然,在这个方法还有更多的操作,比如对请求参数的脱敏和加密等。
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
public class AuthenticationFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化代码,比如加载过滤器配置参数
System.out.println("initFilter");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("Filter1开始");
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 获取请求头中的Authorization字段
String authToken = httpRequest.getHeader("Authorization");
if (authToken == null || "".equals(authToken)) {
// 用户未登录,重定向到登录页面
// httpResponse.sendRedirect(httpRequest.getContextPath() + "/login");
log.info("******用户未登录******");
} else {
// 用户已登录,继续请求的处理
chain.doFilter(request, response);
System.out.println("Filter1结束");
}
}
@Override
public void destroy() {
// 清理代码,如关闭资源
System.out.println("destroy");
}
}
2)将自己的过滤器注入到Spring容器中,并配置拦截路径
@Bean
public FilterRegistrationBean registerAuthenticationFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean(new AuthenticationFilter());
bean.addUrlPatterns("/*");
bean.setOrder(1);
return bean;
}
四、生命周期
Filter的生命周期主要包含以下三个阶段:
-
初始化(Initialization)
- 当Web容器(如Tomcat)启动时,或者第一次访问与Filter关联的资源时,容器会创建Filter实例。这个过程只会发生一次。
- 在创建Filter实例之后,容器会调用Filter的
init(FilterConfig filterConfig)
方法进行初始化。FilterConfig
对象包含了Filter的初始化参数,比如从web.xml或注解中获取的配置信息。这个阶段通常用于读取配置信息和执行Filter需要的初始化操作。
-
过滤操作(Filtering)
- 每次请求匹配Filter的映射时,容器会调用Filter的
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
方法。 - 在
doFilter
方法中,Filter可以执行它的主要工作,比如检查请求头、对请求或响应数据进行修改、记录日志等。 - Filter必须显式调用
FilterChain
的doFilter
方法,将请求和响应对象传递给链中的下一个元素。如果不调用doFilter
方法,则请求处理流程会在当前Filter停止,后续的Filter和Servlet不会被执行。 doFilter
方法是Filter生命周期中被多次调用的部分,每次请求只要符合Filter的映射条件,这个方法就会被调用。
- 每次请求匹配Filter的映射时,容器会调用Filter的
-
销毁(Destruction)
- 当Web容器关闭或应用程序被卸载时,容器会调用Filter的
destroy()
方法。这个方法是Filter生命周期的最后一个方法,用于执行清理操作,比如释放资源、关闭数据库连接等。 - 在
destroy()
方法调用之后,Filter实例将被垃圾回收器回收。
- 当Web容器关闭或应用程序被卸载时,容器会调用Filter的
五、过滤器链
Java中的Filter过滤器链是一种在服务器端程序中常用的设计模式,它主要用于在请求到达Servlet之前对请求进行预处理,以及在响应离开Servlet之后对响应进行后处理。通过使用过滤器链,可以灵活地添加或移除处理请求和响应的不同过滤器,实现了解耦和复用
* 通过`setOrder()`方法可以调整过滤器的执行顺序
@GetMapping("/test/filter")
public String filter(@RequestParam String name, @RequestParam Integer age) {
System.out.println("业务代码开始执行");
return "姓名:" + name + "\t 年龄:" + age;
}
- 这一段业务代码,我们在此路径上设置了两个过滤器,通过setOrder调整顺序,一起来看看效果吧: