好的,没问题!咱们今天就来聊聊 Spring Boot 里两个好玩又实用的家伙:Web 过滤器 (Filter) 和拦截器 (Interceptor)。它们就像是 Spring Boot 这辆跑车上的安全带和导航仪,帮你规范请求,保驾护航。
文章大纲
- 开场白:别让你的 Spring Boot 应用裸奔!
- Web 过滤器 (Filter):请求的“守门员”
- 2.1 什么是 Web 过滤器?
- 2.2 过滤器的生命周期
- 2.3 如何自定义一个过滤器?
- 2.4 过滤器的典型应用场景
- 2.5 过滤器链:层层把关
- 2.6 过滤器配置:让 Spring Boot 找到你
- 2.7 示例:实现一个简单的日志过滤器
- 拦截器 (Interceptor):请求的“导航员”
- 3.1 什么是拦截器?
- 3.2 拦截器的生命周期(三大方法)
- 3.3 如何自定义一个拦截器?
- 3.4 拦截器的典型应用场景
- 3.5 拦截器配置:告诉 Spring Boot 往哪儿拦
- 3.6 示例:实现一个权限验证拦截器
- 过滤器 vs. 拦截器:傻傻分不清楚?
- 4.1 相同点
- 4.2 不同点
- 4.3 如何选择?
- 高级用法:玩转过滤器和拦截器
- 5.1 过滤器中的异步处理
- 5.2 拦截器中的异常处理
- 5.3 使用 HandlerMethodArgumentResolver 自定义参数解析
- 5.4 使用 HandlerMethodReturnValueHandler 自定义返回值处理
- 最佳实践:让你的代码更优雅
- 6.1 职责单一原则
- 6.2 避免过度设计
- 6.3 善用配置
- 总结:过滤器和拦截器,Spring Boot 的左膀右臂
1. 开场白:别让你的 Spring Boot 应用裸奔!
想象一下,你辛辛苦苦写了一个 Spring Boot 应用,对外提供各种 API 接口。如果没有任何保护措施,就像让它在大街上裸奔一样,随便什么人都能访问,想想都觉得可怕。
- 安全风险:恶意用户可以随意调用你的接口,窃取数据、破坏系统。
- 性能问题:大量无效请求会消耗你的服务器资源,拖慢系统速度。
- 业务逻辑混乱:很多通用的逻辑(比如日志记录、权限验证)散落在各个 Controller 中,代码冗余,难以维护。
这个时候,Web 过滤器和拦截器就派上用场了。它们就像是你的应用的“保安”和“向导”,帮你过滤掉恶意请求,规范用户行为,让你的 Spring Boot 应用更加安全、高效、易于维护。
2. Web 过滤器 (Filter):请求的“守门员”
2.1 什么是 Web 过滤器?
Web 过滤器是 Servlet 规范中的一个组件,它可以在请求到达 Servlet (比如 Spring MVC 的 DispatcherServlet) 之前或之后,对请求进行预处理或后处理。你可以把过滤器想象成一个“守门员”,它会检查每一个进来的请求,看看是否符合规则,如果不符合,就直接拒绝或者进行一些处理。
2.2 过滤器的生命周期
过滤器的生命周期由 Servlet 容器(比如 Tomcat)管理,它有三个关键方法:
init(FilterConfig filterConfig)
:在过滤器创建时被调用,用于初始化过滤器。doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
:这是过滤器的核心方法,每个请求都会经过这里。你可以在这里编写你的过滤逻辑。destroy()
:在过滤器销毁时被调用,用于释放资源。
2.3 如何自定义一个过滤器?
要自定义一个过滤器,你需要:
- 创建一个类,实现
javax.servlet.Filter
接口。 - 重写
init()
、doFilter()
和destroy()
方法。 - 使用
@Component
注解将你的过滤器注册为 Spring Bean,或者使用FilterRegistrationBean
进行更细粒度的控制。
2.4 过滤器的典型应用场景
- 日志记录:记录每个请求的 URL、IP 地址、请求参数等信息。
- 权限验证:检查用户是否登录,是否有权限访问某个资源。
- 字符编码转换:将请求的字符编码统一转换为 UTF-8。
- XSS 攻击防御:过滤掉请求中的恶意脚本。
- GZIP 压缩:对响应进行 GZIP 压缩,减少网络传输量。
2.5 过滤器链:层层把关
你可以配置多个过滤器,它们会按照一定的顺序组成一个过滤器链 (FilterChain)。每个过滤器都会对请求进行处理,然后将请求传递给下一个过滤器,直到最后一个过滤器处理完成,请求才会到达 Servlet。
2.6 过滤器配置:让 Spring Boot 找到你
在 Spring Boot 中,你可以使用以下两种方式配置过滤器:
- 使用
@Component
注解:这是最简单的方式,直接将你的过滤器类标记为@Component
,Spring Boot 会自动注册它。 - 使用
FilterRegistrationBean
:这种方式可以让你更灵活地控制过滤器的注册,比如指定过滤器的 URL 匹配规则、执行顺序等。
2.7 示例:实现一个简单的日志过滤器
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
public class LogFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(LogFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("LogFilter 初始化");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String url = httpRequest.getRequestURL().toString();
String method = httpRequest.getMethod();
logger.info("请求URL: {}, 请求方法: {}", url, method);
// 继续执行过滤器链,将请求传递给下一个过滤器或 Servlet
chain.doFilter(request, response);
}
@Override
public void destroy() {
logger.info("LogFilter 销毁");
}
}
这个例子中,LogFilter
会记录每个请求的 URL 和请求方法。
3. 拦截器 (Interceptor):请求的“导航员”
3.1 什么是拦截器?
拦截器是 Spring MVC 框架中的一个组件,它可以在请求到达 Controller 之前、Controller 处理请求之后、视图渲染之后,对请求进行拦截和处理。你可以把拦截器想象成一个“导航员”,它会引导请求按照正确的路线到达目的地,并在途中进行一些必要的处理。
3.2 拦截器的生命周期(三大方法)
拦截器有三个关键方法:
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
:在 Controller 处理请求之前被调用。你可以在这里进行权限验证、参数校验等操作。如果返回false
,则请求不会被继续处理。postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
:在 Controller 处理请求之后,但在视图渲染之前被调用。你可以在这里修改 ModelAndView,添加一些额外的参数。afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
:在视图渲染之后被调用。你可以在这里进行资源清理、异常处理等操作。
3.3 如何自定义一个拦截器?
要自定义一个拦截器,你需要:
- 创建一个类,实现
org.springframework.web.servlet.HandlerInterceptor
接口。 - 重写
preHandle()
、postHandle()
和afterCompletion()
方法。 - 注册你的拦截器到 Spring MVC 的配置中。
3.4 拦截器的典型应用场景
- 权限验证:检查用户是否登录,是否有权限访问某个资源。
- 日志记录:记录请求的执行时间、用户信息等。
- 国际化处理:根据用户的语言设置,设置相应的 Locale。
- 防止重复提交:防止用户重复提交表单。
- Open API 签名验证:验证请求的签名是否合法。
3.5 拦截器配置:告诉 Spring Boot 往哪儿拦
你需要创建一个配置类,实现 org.springframework.web.servlet.config.annotation.WebMvcConfigurer
接口,并重写 addInterceptors()
方法,将你的拦截器注册到 Spring MVC 中。
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 WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor()) // 添加拦截器
.addPathPatterns("/api/**") // 拦截的 URL
.excludePathPatterns("/api/login"); // 排除的 URL
}
}
3.6 示例:实现一个权限验证拦截器
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
import javax.servlet.http.HttpSession;
@Component
public class AuthInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(AuthInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
Object user = session.getAttribute("user");
if (user == null) {
logger.warn("用户未登录,禁止访问: {}", request.getRequestURI());
response.sendRedirect("/login"); // 重定向到登录页面
return false; // 阻止请求继续执行
}
return true; // 允许请求继续执行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("请求处理完毕,准备渲染视图");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("视图渲染完毕");
}
}
这个例子中,AuthInterceptor
会检查用户是否登录,如果没有登录,就重定向到登录页面。
4. 过滤器 vs. 拦截器:傻傻分不清楚?
Web 过滤器和拦截器都是用于处理请求的组件,但它们之间有一些重要的区别。
4.1 相同点
- 都可以对请求进行预处理和后处理。
- 都可以实现权限验证、日志记录等功能。
4.2 不同点
特性 | Web 过滤器 (Filter) | 拦截器 (Interceptor) |
---|---|---|
实现方式 | Servlet 规范 | Spring MVC 框架 |
作用范围 | 作用于所有进入 Servlet 容器的请求 | 作用于 Spring MVC 的 Controller 请求 |
执行顺序 | 在 DispatcherServlet 之前或之后执行 | 在 Controller 的方法执行之前、之后、视图渲染之后执行 |
依赖关系 | 不依赖 Spring 容器 | 依赖 Spring 容器 |
功能 | 更多的是通用的、底层的处理,比如字符编码转换、XSS 防御 | 更多的是业务相关的处理,比如权限验证、日志记录、参数校验 |
获取 Spring Bean | 需要手动从 Spring 容器中获取,比较麻烦 | 可以直接注入 Spring Bean |
4.3 如何选择?
- 如果你的处理逻辑是通用的、底层的,不依赖 Spring 容器,比如字符编码转换、XSS 防御,那么选择过滤器。
- 如果你的处理逻辑是业务相关的,需要访问 Spring Bean,比如权限验证、日志记录、参数校验,那么选择拦截器。
5. 高级用法:玩转过滤器和拦截器
5.1 过滤器中的异步处理
Servlet 3.0 引入了异步处理的功能,你可以在过滤器中使用异步处理来提高性能。
@Component
public class AsyncFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
AsyncContext asyncContext = request.startAsync();
asyncContext.start(() -> {
try {
// 模拟耗时操作
Thread.sleep(2000);
ServletResponse asyncResponse = asyncContext.getResponse();
asyncResponse.getWriter().write("Async Filter 处理完成");
asyncContext.complete();
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
5.2 拦截器中的异常处理
你可以在 afterCompletion()
方法中处理 Controller 抛出的异常。
@Component
public class ExceptionInterceptor implements HandlerInterceptor {
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if (ex != null) {
// 记录异常信息
logger.error("发生异常: {}", ex.getMessage());
// 可以根据不同的异常类型进行不同的处理
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().write("服务器内部错误");
}
}
}
5.3 使用 HandlerMethodArgumentResolver 自定义参数解析
HandlerMethodArgumentResolver
允许你自定义 Controller 方法的参数解析逻辑。比如,你可以从请求头中获取用户信息,并将其注入到 Controller 方法的参数中。
5.4 使用 HandlerMethodReturnValueHandler 自定义返回值处理
HandlerMethodReturnValueHandler
允许你自定义 Controller 方法的返回值处理逻辑。比如,你可以将 Controller 方法的返回值统一包装成 JSON 格式。
6. 最佳实践:让你的代码更优雅
6.1 职责单一原则
每个过滤器和拦截器都应该只负责一个特定的功能,避免将多个功能耦合在一起。
6.2 避免过度设计
不要为了追求“完美”而过度设计你的过滤器和拦截器,保持简单易懂。
6.3 善用配置
使用 Spring Boot 的配置功能,将一些可配置的参数提取到配置文件中,方便修改和维护。
7. 总结:过滤器和拦截器,Spring Boot 的左膀右臂
Web 过滤器和拦截器是 Spring Boot 应用中非常重要的两个组件。它们可以帮助你实现各种通用的、业务相关的处理逻辑,提高应用的安全性、性能和可维护性。掌握它们的使用方法,可以让你的 Spring Boot 应用更加健壮、高效。
希望这篇文章能帮助你更好地理解和使用 Web 过滤器和拦截器。祝你编程愉快!