SpringBoot如何设置无注解的Filter顺序

问题描述 投票:0回答:4

我试图在弹簧过滤器链中插入(在第一个位置)一个简单的自定义 Cors 过滤器。

如果我这样做

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilter implements Filter {

它完美地工作 我可以通过在 Spring 的 ServletHandler.java 中放置一个断点来验证它,那里有行

chain=getFilterChain(baseRequest, target, servlet_holder);

我只是想知道我是否希望 not 使用

@Componenent and @Order
而不是在应用程序上下文中定义 Filter bean。 如何设置过滤器的顺序?

spring filter spring-boot annotations
4个回答
6
投票

参见示例: 在你的班级 ServletInitializer:

@Bean
 public FilterRegistrationBean requestLogFilter() {
        final FilterRegistrationBean reg = new FilterRegistrationBean(createRequestLogFilter());
        reg.addUrlPatterns("/*");
        reg.setOrder(1); //defines filter execution order
        return reg;
 }

 @Bean
 public RequestLogFilter createRequestLogFilter(){
        return new RequestLogFilter();
 }

我的过滤器的名称是“requestLogFilter”

警告:不要在 Filter 类中使用 @Component 注解。


3
投票

对于 corsFilter,通常需要将此过滤器的加载顺序设置在 springSecurityFilterChain 和其他过滤器(例如 spring boot 中的 errorPageFilter)之前加载到链的最开头。否则 CORS 支持将被破坏,并且不会引入所需的标头。这可以通过以下事情来完成:

@Configuration
public class MyConfiguration {

    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://domain1.com");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(Integer.MIN_VALUE);
        return bean;
    }
}

bean.setOrder(Integer.MIN_VALUE) 将确保这是第一个加载的过滤器。即使还有其他过滤器,例如 order 设置为 Integer.MIN_VALUE(-2147483648) 的 errorPage 过滤器。


2
投票

另一种选择是让您的过滤器实现

org.springframework.core.Ordered
,如果您使用的是 spring webflux(它没有
FilterRegistrationBean
),这是唯一的选择:

public class CorsFilter implements Filter, Ordered {
    //...

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE
    }
}

或者您甚至可以将其设为过滤器中的变量。


0
投票

您的问题的简短回答:

@Configuration
@ComponentScan
@ConfigurationPropertiesScan
class CommonConfig {
    @Autowired
    lateinit var chains: List<SecurityFilterChain>

    @Autowired
    lateinit var myCustomFilter: MyCustomFilter

    @PostConstruct
    fun post() {
        chains.forEach {
            for (i in it.filters.indices){
                if (it.filters[i] is LogoutFilter) {
                    it.filters.add(i + 1, myCustomFilter)
                }
            }
        }
    }
}

更多详情如下:

假设我们有以下链:

@Bean
@Throws(Exception::class)
fun filterChain(http: HttpSecurity): SecurityFilterChain {
    http.authorizeRequests()
        .antMatchers(HttpMethod.POST, "/first", "/second")
        .authenticated()
        .anyRequest()
        .permitAll()
        .and()
        .httpBasic()
        .authenticationEntryPoint(auditAuthenticationEntryPoint)
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and().formLogin().disable()
        .csrf().disable()
        .cors().configurationSource(corsConfigurationSource())
    return http.build()
}

我们想在一些特定过滤器之间的这个链中添加以下过滤器:

@Component
class MyCustomFilter : OncePerRequestFilter() {
    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        //some work: adding headers, working with authentication or something else
        filterChain.doFilter(request, response)
    }
}

这里有几种方法可以将我们的过滤器添加到链中:

1)在过滤器中添加@Order注解:

@Component
@Order(5)
class MyCustomFilter : OncePerRequestFilter()

注意:

  • 如果您没有在注释中指定顺序,则默认顺序为:
    Ordered.LOWEST_PRECEDENCE
    - 您的过滤器将是链中的最后一个。
  • 如果您在过滤器中使用身份验证(通过身份验证提供程序),那么
    Ordered.LOWEST_PRECEDENCE
    Ordered.HIGHEST_PRECEDENCE
    不适合您的情况,因为:
    • 万一
      Ordered.HIGHEST_PRECEDENCE
      过滤器
      SecurityContextPersistenceFilter
      将在您之后执行 过滤器将清除 SecurityContext,即使您的过滤器将 Authentication 对象添加到 SecurityContext
    • Ordered.LOWEST_PRECEDENCE
      的情况下,您的过滤器根本不会被调用:AnonymousAuthenticationFilter 会将 Authentication 对象设置为 匿名,您将在连锁店调用您的过滤器之前收到 401 或 403 响应
    • 在这种情况下,您需要获取链并确定您的过滤器的具体顺序 通过
      FilterChainProxy
      doFilter(ServletRequest request, ServletResponse response)
      方法,得到
      this.additionalFilters
      。例如,
      5
      。 但这不是确定顺序的最佳方法

2)标准(也是最好)的方法是通过设置过滤器链本身:

    @Bean
    @Throws(Exception::class)
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http.authorizeRequests()
            ...
            .addFilterBefore(MyCustomFulter(), BasicAuthenticationFilter::class.java)
            ...
        return http.build()
    }

3) 在@PostConstruct 中将过滤器注入链中

在我的例子中,.addFilterBefore() 是不可能的,因为 过滤器是从启动器添加的。我的启动器需要添加一个过滤器 到 LogoutFilter 之后的每个链。因为可以有多个链 一个上下文,我为每个添加一个过滤器。因此,将我的启动器添加到应用程序时 ,过滤器添加到需要的地方,无需任何额外配置:

@Configuration
@ComponentScan
@ConfigurationPropertiesScan
class CommonConfig {
    @Autowired
    lateinit var chains: List<SecurityFilterChain>

    @Autowired
    lateinit var myCustomFilter: MyCustomFilter

    @PostConstruct
    fun post() {
        chains.forEach {
            for (i in it.filters.indices){
                if (it.filters[i] is LogoutFilter) {
                    it.filters.add(i + 1, myCustomFilter)
                }
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.