如何在早期 servlet/spring 引导过滤器中强制结束过滤器链

How to forcefully end filter-chain in an early servlet/spring boot filter

有一个多租户应用程序,我想尽早结束任何没有租户的请求。

设置/场景

我 运行 spring 使用 spring 会话和 spring 安全启动(网络)。

在我的例子中,我有几种策略来确定租户,TenantFromX,TenantFromY .. 所有 运行ning 按顺序 Ordered.HIGHEST_PRECEDENCE - 所以越早越好。

订单 Ordered.HIGHEST_PRECEDENCE+1 我 运行 TenantMustExistFilter 过滤器进行验证,任何策略确实匹配/找到租户定义

@Log4j2
@Component
@RequiredArgsConstructor
// After TenantFromX(min) but before SessionRepositoryFilter(Session) which is min+50
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class TenantMandatoryFilter extends GenericFilterBean
{
  @Override
  public void doFilter(
    ServletRequest request, ServletResponse response,
    FilterChain filterChain
  ) throws ServletException, IOException
  {
    if (TenantStore.getInitMode().isEmpty()) {
      throw new NoTenantException("No tenant identified for request - request blocked");
    }

    try {
      filterChain.doFilter(request, response);
    } finally {
      TenantStore.clear();
    }
  }
}

问题/难题

尽管我抛出运行时异常 NoTenantException 无论如何都会调用该过滤器之后的所有其他过滤器。

例如 SessionRepositoryFilterOrdered.HIGHEST_PRECEDENCE+50 上调用,然后 spring 安全性 FilterProxyChain 被触发。

如何在TenantMandatoryFilter验证失败后有效地停止链? 我希望 return;Exception 或者只是不调用 filterChain.doFilter(request, response); 就足以结束链执行。

我是不是对这里的流程或者思路有什么误解?

尝试次数

我可以通过替换成功结束链

if (TenantStore.getInitMode().isEmpty()) {
  throw new NoTenantException("No tenant identified for request - request blocked");
}

if (TenantStore.getInitMode().isEmpty()) {
  response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
  return;
}

有趣的是,下面的代码将不起作用,尽管它在语义上看起来更正确。它不起作用意味着,它会在之后调用 SessionRepositoryFilterFilterChainProxy

if (TenantStore.getInitMode().isEmpty()) {
  response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid tenant");
  return;
}

我认为这是因为 sendError 将响应设置为暂停以完成任何其他链(这是我不想要的)。

看起来还是不对,所以如何正确地做到这一点?或者这完全是概念上的缺陷?

通过显式调用或抛出异常发送错误将导致将请求分派到应用程序的错误页面处理程序,后者将再次为该分派调用过滤器。设置状态是正确的做法。