SpringBoot - OncePerRequestFilter的使用场景
写在前面
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;
}
}
更多推荐
所有评论(0)