Class TenantContextCleanupFilter

java.lang.Object
org.springframework.web.filter.GenericFilterBean
org.springframework.web.filter.OncePerRequestFilter
io.leandev.appfuse.security.tenant.TenantContextCleanupFilter
All Implemented Interfaces:
jakarta.servlet.Filter, org.springframework.beans.factory.Aware, org.springframework.beans.factory.BeanNameAware, org.springframework.beans.factory.DisposableBean, org.springframework.beans.factory.InitializingBean, org.springframework.context.EnvironmentAware, org.springframework.core.env.EnvironmentCapable, org.springframework.web.context.ServletContextAware

public class TenantContextCleanupFilter extends org.springframework.web.filter.OncePerRequestFilter

租戶上下文清理 Filter

確保每個 HTTP 請求結束後清理 TenantContext 的 ThreadLocal, 防止在使用 Thread Pool 時發生跨請求的租戶 ID 洩漏。

為什麼需要這個 Filter?

  • Servlet 容器(如 Tomcat)使用 Thread Pool 處理請求
  • 如果某個請求設定了 ThreadLocal 但未清理,下一個請求可能會讀取到錯誤的租戶 ID
  • 這是一個潛在的安全風險,可能導致跨租戶數據洩漏

配置方式

方式 1:透過 @Bean 註冊(推薦)

@Configuration
public class TenantConfig {
    @Bean
    public FilterRegistrationBean<TenantContextCleanupFilter> tenantContextCleanupFilter() {
        FilterRegistrationBean<TenantContextCleanupFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new TenantContextCleanupFilter());
        registration.addUrlPatterns("/*");
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return registration;
    }
}

方式 2:透過 @Component 自動註冊

// 在應用層繼承並加上 @Component
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class AppTenantContextCleanupFilter extends TenantContextCleanupFilter {
}
See Also:
  • Field Summary

    Fields inherited from class org.springframework.web.filter.OncePerRequestFilter

    ALREADY_FILTERED_SUFFIX

    Fields inherited from class org.springframework.web.filter.GenericFilterBean

    logger
  • Constructor Summary

    Constructors
    Constructor
    Description
    預設建構子
    TenantContextCleanupFilter(boolean debugEnabled)
    建構子
  • Method Summary

    Modifier and Type
    Method
    Description
    protected void
    doFilterInternal(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, jakarta.servlet.FilterChain filterChain)
     
    boolean
    是否記錄 debug 日誌
    void
    setDebugEnabled(boolean debugEnabled)
    設定是否記錄 debug 日誌

    Methods inherited from class org.springframework.web.filter.OncePerRequestFilter

    doFilter, doFilterNestedErrorDispatch, getAlreadyFilteredAttributeName, isAsyncDispatch, isAsyncStarted, shouldNotFilter, shouldNotFilterAsyncDispatch, shouldNotFilterErrorDispatch

    Methods inherited from class org.springframework.web.filter.GenericFilterBean

    addRequiredProperty, afterPropertiesSet, createEnvironment, destroy, getEnvironment, getFilterConfig, getFilterName, getServletContext, init, initBeanWrapper, initFilterBean, setBeanName, setEnvironment, setServletContext

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • TenantContextCleanupFilter

      public TenantContextCleanupFilter()
      預設建構子
    • TenantContextCleanupFilter

      public TenantContextCleanupFilter(boolean debugEnabled)
      建構子
      Parameters:
      debugEnabled - 是否記錄 debug 日誌
  • Method Details

    • doFilterInternal

      protected void doFilterInternal(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, jakarta.servlet.FilterChain filterChain) throws jakarta.servlet.ServletException, IOException
      Specified by:
      doFilterInternal in class org.springframework.web.filter.OncePerRequestFilter
      Throws:
      jakarta.servlet.ServletException
      IOException
    • setDebugEnabled

      public void setDebugEnabled(boolean debugEnabled)
      設定是否記錄 debug 日誌
      Parameters:
      debugEnabled - 是否啟用
    • isDebugEnabled

      public boolean isDebugEnabled()
      是否記錄 debug 日誌
      Returns:
      是否啟用