SpringBoot - OncePerRequestFilter的使用场景

OncePerRequestFilter是Spring提供的一种过滤器实现,确保外部请求只执行一次过滤逻辑,避免服务器内部forward导致的重复过滤。本文介绍了过滤器的基本概念,OncePerRequestFilter的工作原理,以及如何在实际项目中使用它来处理JWT认证。同时,展示了如何通过配置使其在多过滤器环境中按指定顺序执行。
摘要由CSDN通过智能技术生成

写在前面

OncePerRequestFilter类是一个实现了javax.servlet.Filter原生接口的抽象类。OncePerRequestFilter可以保证一次外部请求,只执行一次过滤方法,对于服务器内部之间的forward等请求,不会再次执行过滤方法。

什么是过滤器

在这里插入图片描述
在讲解"OncePerRequestFilter"前,我们先看一下过滤器是什么?

A. 过滤器在WEB应用启动时初始化一次, 在WEB应用停止时销毁;
B. 可以对请求的URL进行过滤, 对敏感词过滤;
C. 过滤器将处理逻辑挡在拦截器的外面;
D. 过滤器实现的是 javax.servlet.Filter原生接口,过滤器是SERVLET 规范的一部分;
E. 过滤器在请求进入WEB容器后,到达SERVLET之前进行过滤器的逻辑处理;
F. 过滤器依赖WEB容器,也会被多次执行。
G. 过滤器的作用就是在业务逻辑执行前/后对请求和响应进行相应的处理,如果能配合HttpServletRequestWrapper和HttpServletResponseWrapper使用更加完美。

什么是单次过滤器

OncePerRequestFilter类是一个实现了javax.servlet.Filter原生接口的抽象类。OncePerRequestFilter可以保证一次外部请求,只执行一次过滤方法,对于服务器内部之间的forward等请求,不会再次执行过滤方法。

使用场景

OncePerRequestFilter的主要目的是为了兼容不同的WEB容器,因为Servlet版本不同,执行的过程也不同,其实不是所有的容器一次请求只过滤一次。

源码分析

public abstract class OncePerRequestFilter extends GenericFilterBean {
     
    ...
    
    public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
            HttpServletRequest httpRequest = (HttpServletRequest)request;
            HttpServletResponse httpResponse = (HttpServletResponse)response;
            
            // 获取是否执行过滤方法的一个属性KEY,作为是否执行过滤方法的一个标记
            String alreadyFilteredAttributeName = this.getAlreadyFilteredAttributeName();
            
            // 检测当前请求的属性中该标记的值是否为空,如果不为空则说明已执行过滤方法了
            boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;
            if (!this.skipDispatch(httpRequest) && !this.shouldNotFilter(httpRequest)) {
                // 已执行
                if (hasAlreadyFilteredAttribute) {
                    if (DispatcherType.ERROR.equals(request.getDispatcherType())) {
                        this.doFilterNestedErrorDispatch(httpRequest, httpResponse, filterChain);
                        return;
                    }

                    filterChain.doFilter(request, response);
                } else {
                // 未执行
                    request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);

                    try {
                    	// 这个抽象方法需要子类去实现具体的过滤逻辑
                        this.doFilterInternal(httpRequest, httpResponse, filterChain);
                    } finally {
                    	// 执行完毕移除标记
                        request.removeAttribute(alreadyFilteredAttributeName);
                    }
                }
            } else {
                filterChain.doFilter(request, response);
            }

        } else {
            throw new ServletException("OncePerRequestFilter just supports HTTP requests");
        }
    }
    ...
}

如何使用

当有多个过滤器时,可以使用@Component+@Order()来设置过滤器的执行顺序。

@Component
public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) {
            tokenService.verifyToken(loginUser);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        chain.doFilter(request, response);
    }
}

官方推荐

SpringBoot官方推荐的重复执行的FILTER只执行一次的解决方案

@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {
    @Bean
    public FilterRegistrationBean<MyFilter> registration(MyFilter filter) {
        FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(filter);
        registration.setEnabled(false);
        return registration;
    }
}
Spring Boot是一个用于构建Java应用程序的开发框架,它简化了Java开发过程中的许多繁琐任务。拦截器是Spring Boot中一种常用的组件,用于在请求处理的不同阶段进行拦截和处理。 在Spring Boot中,可以使用拦截器来实现对请求的预处理、后处理以及错误处理等功能。拦截器可以继承自OncePerRequestFilter类,该类是Spring提供的一个抽象类,用于确保每个请求只被拦截一次。 继承OncePerRequestFilter类后,需要重写doFilterInternal方法,在该方法中编写具体的拦截逻辑。doFilterInternal方法接收HttpServletRequest和HttpServletResponse对象作为参数,可以通过这两个对象来获取请求信息和进行相应的处理操作。 以下是使用拦截器继承OncePerRequestFilter的示例代码: ```java import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyInterceptor extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 在这里编写拦截逻辑 // 可以对请求进行预处理、后处理等操作 // 调用filterChain.doFilter方法将请求传递给下一个过滤器或处理器 filterChain.doFilter(request, response); } } ``` 使用拦截器继承OncePerRequestFilter时,需要将其注册到Spring Boot应用程序中。可以通过@Configuration注解和@Bean注解来实现拦截器的注册,具体的配置方式可以根据实际需求进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cloneme01

谢谢您的支持与鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值