保卫你的应用:探索过滤器和拦截器的奥秘
- 前言
- 概述
- 实现原理不同
- 使用范围不同
- 触发时机不同
- 注入Bean情况不同
- springboot中的实现
前言
在现代Web开发中,安全性和性能是至关重要的因素。过滤器和拦截器是Web应用中的两个关键概念,它们可以帮助你保护你的应用免受恶意攻击,同时还可以实现各种功能,从日志记录到性能优化。这两个概念可能听起来有些抽象,但实际上它们就像是你应用的护卫者,站在前线,确保一切都在掌握之中。在本文中,我们将深入探讨过滤器和拦截器的作用、差异以及如何在你的项目中充分利用它们。
概述
拦截器和过滤器都使用了AOP的编程思想,都可以实现诸如日志记录、登录鉴权等功能,但二者的不同点也是比较多的
过滤器(Filters)和拦截器(Interceptors)是在Web开发中用于处理HTTP请求和响应的关键组件,它们用于执行一些特定的任务,如请求预处理、响应处理、日志记录、安全验证等。以下是它们的基本介绍以及在Web开发中的角色:
过滤器(Filters):
- 基本介绍:过滤器是Servlet规范中的一部分,它是一个可插入的组件,用于在请求进入Servlet容器之前或响应离开容器之后对请求和响应进行预处理或后处理。
- 角色:过滤器的主要角色是对HTTP请求和响应进行全局性的预处理和后处理。它们可以用于执行以下任务:
- 认证和授权:验证用户身份并授予权限。
- 日志记录:记录请求和响应的信息,以便监控和分析。
- 数据压缩:在服务器端压缩响应数据以减小带宽占用。
- 编码转换:将请求和响应的字符编码转换为指定的格式。
- 请求参数处理:对请求参数进行验证、过滤或修改。
- 缓存控制:管理浏览器缓存,提高性能。
拦截器(Interceptors):
- 基本介绍:拦截器是在Spring框架中的一部分,它提供了一种机制,允许开发人员在请求处理的不同阶段执行自定义逻辑。
- 角色:拦截器的主要角色是在Spring框架中对请求和响应进行拦截和处理。它们通常用于以下任务:
- 权限验证:验证用户是否有权限执行某个操作。
- 日志记录:记录请求和响应的信息,以进行审计或分析。
- 性能监测:测量请求处理的性能,检测潜在的性能问题。
- 异常处理:捕获并处理请求处理中的异常。
- 国际化:处理多语言支持和区域设置。
- 缓存控制:管理响应的缓存策略。
区别和选择:
- 过滤器是Servlet规范的一部分,而拦截器是Spring框架的一部分。因此,如果你使用Servlet容器(如Tomcat)来开发Web应用,你可以使用过滤器。如果使用Spring框架,可以选择使用拦截器。
- 过滤器是全局性的,它们在Servlet容器级别操作,而拦截器是基于Spring MVC框架的,它们在控制器级别操作。
- 过滤器通常更适用于低级别的任务,如字符编码、缓存控制等。拦截器通常更适合于高级别的任务,如权限验证、日志记录等。
- 在实际应用中,可以根据任务的性质和框架选择来决定是使用过滤器还是拦截器,有时两者可以结合使用以实现不同层次的处理。
实现原理不同
拦截器和过滤器底层实现方式大不相同,过滤器是基于函数回调的,拦截器则是基于java的反射机制(动态代理)实现的。
我们自定义的过滤器中都会实现一个doFIlter()方法,这个方法有一个FilterChain参数,而实际上它是一个回调接口。Application FilterChain是它的实现类,这个实现类内部也有一个doFilter()方法就是回调方法。
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
ApplicationFilterChain里面能拿到我们自定义的xxxFilter类,在其内部回调方法doFilter()里面调用各个自定义xxxFilter过滤器,并执行doFilter()方法
public final class ApplicationFilterChain implements FilterChain {
@Override
public void doFilter(ServletRequest request, ServletResponse response) {
...//省略
internalDoFilter(request,response);
}
private void internalDoFilter(ServletRequest request, ServletResponse response){
if (pos < n) {
//获取第pos个filter
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
...
filter.doFilter(request, response, this);
}
}
}
每个xxxFilter会先执行自身的doFilter()过滤逻辑,最后在执行结束前会执行filterChain.doFilter(servletRequest,servletResponse),也就是回调Application FilterChain的doFilter()方法,以此循环执行实现函数回调。
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest, servletResponse);
}
使用范围不同
我们看到过滤器实现的是javax.servlet.Filter接口,而这个接口是在servlet规范中定义的,也就是说过滤器Filter的使用要依赖于Tomcat等容器,导致它只能在web程序中用。
而拦截器(Interceptor)它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器。是可以单独使用的。不仅能应用在web程序汇总,也可以用于Application、Swing等程序中。
过滤器(Filters)和拦截器(Interceptors)在使用范围上有一些不同,这取决于它们所属的技术框架以及其执行的阶段和任务。以下是它们的使用范围的不同之处:
过滤器(Filters)的使用范围:
-
Servlet容器级别:过滤器是Servlet规范的一部分,因此它们在Servlet容器级别操作。这意味着它们可以应用于任何基于Servlet的Web应用程序,不依赖于特定的框架。
-
低级别任务:过滤器通常用于处理HTTP请求和响应的低级别任务,如字符编码、缓存控制、压缩、请求参数处理等。它们可以修改请求和响应的底层内容。
-
全局性处理:过滤器能够对整个Web应用程序的请求和响应进行全局性处理,因此可以应用于多个URL模式下的请求。
拦截器(Interceptors)的使用范围:
-
Spring MVC框架级别:拦截器是Spring MVC框架的一部分,因此它们在Spring应用程序中使用。拦截器只对Spring MVC控制器处理的请求生效。
-
高级别任务:拦截器通常用于处理与业务逻辑相关的高级别任务,如权限验证、日志记录、性能监测、国际化、异常处理等。它们更专注于业务逻辑。
-
控制器级别处理:拦截器只拦截Spring MVC控制器处理的请求,因此可以根据需要选择性地应用于特定的控制器或URL模式。
总结:
- 过滤器是Servlet规范的一部分,可用于所有基于Servlet的Web应用程序,用于低级别任务,全局性处理。
- 拦截器是Spring MVC框架的一部分,仅适用于Spring应用程序,用于高级别任务,控制器级别处理。
- 选择过滤器或拦截器取决于你的应用程序需求和所使用的技术框架。通常,过滤器用于处理通用的HTTP请求和响应操作,而拦截器用于处理与业务逻辑相关的任务。
触发时机不同
过滤器和拦截器的触发时机也不同,如下图
过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后
拦截器Interceptor是在请求进入servlet后,在进入Controller之前进行预处理的,Controller中渲染了对应的视图之后请求结束。
注入Bean情况不同
在Spring Boot应用程序中,使用过滤器(Filters)和拦截器(Interceptors)时,注入Bean的方式有一些不同,这取决于它们所属的技术框架和生命周期管理。以下是过滤器和拦截器注入Bean的不同之处:
过滤器(Filters)的注入Bean:
-
过滤器是Servlet规范的一部分,它们通常不是由Spring容器管理的Bean。因此,过滤器的生命周期由Servlet容器管理,而不是Spring容器。
-
你不能直接使用Spring的依赖注入(如
@Autowired
)来注入其他Spring Bean到过滤器中,因为过滤器实例通常不受Spring的控制。 -
为了在过滤器中使用Spring的Bean,你可以通过Spring的
WebApplicationContextUtils
工具类获取ApplicationContext
,然后从中获取所需的Bean。
示例代码:
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class MyFilter implements Filter {
private MyService myService;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
ApplicationContext context = WebApplicationContextUtils
.getRequiredWebApplicationContext(filterConfig.getServletContext());
myService = context.getBean(MyService.class);
}
// ...
}
拦截器(Interceptors)的注入Bean:
-
拦截器是Spring MVC框架的一部分,它们通常是由Spring容器管理的Bean。因此,你可以直接使用Spring的依赖注入来注入其他Spring Bean到拦截器中。
-
你可以使用
@Autowired
或@Resource
等注解将其他Spring Bean注入到拦截器中,这使得在拦截器中使用Spring管理的服务和组件变得非常方便。
示例代码:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class MyInterceptor implements HandlerInterceptor {
private MyService myService;
@Autowired
public MyInterceptor(MyService myService) {
this.myService = myService;
}
// ...
}
总结:
- 过滤器通常不是由Spring容器管理的Bean,因此在过滤器中注入Spring Bean 需要使用
WebApplicationContextUtils
等方式来手动获取Spring容器中的Bean。 - 拦截器通常是由Spring容器管理的Bean,因此你可以使用常规的Spring依赖注入方式(如
@Autowired
)来注入其他Spring Bean 到拦截器中,这更加便捷。
springboot中的实现
理解过滤器和拦截器的差异,让我们以一个基于Spring Boot的用户认证示例为例,展示如何使用过滤器和拦截器来实现用户认证。
场景:我们将创建一个Spring Boot Web应用程序,其中包含一个受保护的资源,只有已登录的用户才能访问。我们将使用过滤器和拦截器分别来实现这个认证逻辑。
项目设置:首先,确保你有一个Spring Boot项目的基础设置。
使用过滤器的示例:
- 创建一个自定义过滤器类,例如
AuthenticationFilter
,来检查用户是否已登录。
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class AuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 在这里检查用户是否已登录,例如从Session中获取用户信息
// 如果用户未登录,可以重定向到登录页面
// 如果用户已登录,允许请求继续执行
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化过滤器(可选)
}
@Override
public void destroy() {
// 销毁过滤器(可选)
}
}
- 在Spring Boot应用程序中注册过滤器。
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<AuthenticationFilter> authenticationFilter() {
FilterRegistrationBean<AuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new AuthenticationFilter());
// 设置需要过滤的URL模式
registrationBean.addUrlPatterns("/protected/*"); // 这里假设受保护的资源在/protected/下
return registrationBean;
}
}
使用拦截器的示例:
- 创建一个拦截器类,例如
AuthenticationInterceptor
,来检查用户是否已登录。
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AuthenticationInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 在这里检查用户是否已登录,例如从Session中获取用户信息
// 如果用户未登录,可以重定向到登录页面
// 如果用户已登录,返回true允许请求继续执行,或者返回false阻止请求继续执行
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 请求处理后执行的操作(可选)
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
// 完成请求后执行的操作(可选)
}
}
- 在Spring Boot应用程序中注册拦截器。
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 {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthenticationInterceptor())
.addPathPatterns("/protected/**"); // 这里假设受保护的资源在/protected/下
}
}
在上述示例中,无论你选择使用过滤器还是拦截器,它们都用于拦截请求并执行用户认证逻辑。关于具体的用户认证逻辑(例如如何检查用户是否已登录),需要根据你的应用程序和需求来实现。