【问题标题】:Adding a custom filter to be invoked after spring-security filter in a Servlet 3+ environment在 Servlet 3+ 环境中添加要在 spring-security 过滤器之后调用的自定义过滤器
【发布时间】:2014-08-13 09:43:51
【问题描述】:

我正在使用 Spring-Security 3.2.4 和 Spring Boot 1.1.0(以及它的相关依赖项版本 4.X)。 我正在编写一个将在嵌入式 tomcat 中运行的 Web 应用程序。

我正在尝试添加两个额外的过滤器(与 Spring 安全性无关),其中一个将在 Spring-Security-FilterChainProxy 之前调用,另一个将在 Spring-Security-FilterChainProxy 之后调用。

我的 Spring-Security 配置文件:

@Configuration
@EnableWebMvcSecurity
public class SecurityCtxConfig extends WebSecurityConfigurerAdapter {

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth
    .inMemoryAuthentication()
        .withUser("user").password("pass").roles("USER");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf()
            .disable()
            .authorizeRequests()
            .anyRequest()
            .authenticated()
            .and()
            .formLogin()
            .usernameParameter("user").passwordParameter("password");
}
}

还有主类(Application.class):

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {

    @Bean
RequestFilter beforeSpringSecurityFilter(){
    return new RequestFilter();
}

@Bean
RequestFilter afterSpringSecurityFilter(){
    return new RequestFilter();
}

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

以及过滤器的实现:

public class RequestFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
        FilterChain filterChain) throws ServletException, IOException {
        filterChain.doFilter(request, response);
}

}

考虑到 FilterChainProxy (即由 WebSecurityConfigurerAdapter 创建的蜜蜂)时,有没有办法控制调用顺序? 准确地说,所需的顺序是:

  1. request-filter-1
  2. Spring-Security 过滤链
  3. request-filter-2

谢谢

【问题讨论】:

    标签: spring-mvc spring-security spring-boot


    【解决方案1】:

    在某些时候,spring boot 将安全过滤器暴露为一个属性。这现在很容易做到。

    在你的 application.yml 中:

      spring:
        security:
          filter:
            order: 20
    

    还有一些你想在 Spring Security 完成之后调用的过滤器:

    @Bean
    public FilterRegistrationBean<Filter> afterAuthFilterRegistrationBean() {
        FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
        //a filter that extends OncePerRequestFilter
        AfterAuthFilter afterAuthFilter = new AfterAuthFilter();
        registrationBean.setFilter(afterAuthFilter);
        //this needs to be a number greater than than spring.security.filter.order
        registrationBean.setOrder(30);
        return registrationBean;
    }
    

    对于在 Spring 安全之前执行的过滤器,将 order 设置为小于 20 的数字。

    【讨论】:

      【解决方案2】:

      如果您使用的是 web.xml 方法,您可以按照以下步骤操作: https://stackoverflow.com/a/11929129/1542363

      如果您使用 Java 配置方法,您可以在 WebSecurityConfigurerAdapter 中执行此操作

      @Override
      protected void configure(HttpSecurity http) throws Exception {
      
          http.addFilterBefore(your-request-filter-1, ChannelProcessingFilter.class);
          http.addFilterAfter(your-request-filter-2, SwitchUserFilter.class);
      
      }
      

      始终检查您正在使用的库版本,并参考特定文档以了解过滤器链的正确顺序:

      https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#ns-custom-filters

      或者,如果您使用AbstractSecurityWebApplicationInitializer,您可以使用insertFiltersappendFilters

      public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
      
          @Override
          protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
              insertFilters(servletContext, new MultipartFilter());
          }
      }
      

      更多信息您可以参考: https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-multipart

      【讨论】:

        【解决方案3】:

        同意 Dave Syer 所说的一切;)但希望添加一个使用 FilterRegistrationBean 的 Java Config 示例。

        在我的情况下,我发现我的自定义安全过滤器(使用 Spring Security)每次请求都会被触发两次。添加 FilterRegistrationBean 配置解决了这个问题。

            @Bean(name = "myFilter")
            public MyAuthenticationFilter myAuthenticationFilter(final MyAuthenticationEntryPoint entryPoint) {
                final MyAuthenticationFilter filter = new MyAuthenticationFilter();
                filter.setEntryPoint(entryPoint);
                return filter;
            }
        
            /**
             *  We do this to ensure our Filter is only loaded once into Application Context
             *
             */
            @Bean(name = "authenticationFilterRegistration")
            public FilterRegistrationBean myAuthenticationFilterRegistration(final MyAuthenticationFilter filter) {
                final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
                filterRegistrationBean.setFilter(filter);
                filterRegistrationBean.setEnabled(false);
                return filterRegistrationBean;
            }
        

        (关于我在应用程序上下文中注册两次过滤器的具体问题 - 而不是使用FilterRegistrationBean,我还发现重新实现MyAuthenticationFilter 以从OncePerRequestFilter 继承而不是GenericFilterBean 也有效。但是, OncePerRequestFilter 支持从 Servlet 3.x 开始,由于我正在编写一个公共库,可能需要 Servlet 2.x 的支持)

        【讨论】:

        • MyAuthenticationEntryPoint 到底是什么?
        【解决方案4】:

        Spring Security 使用的FilterChainProxy 不是Ordered(如果是,您可以订购所有过滤器)。但是您应该能够在FilterRegistrationBean 中注册它 Ordered 并以相同的方式注册您的其他过滤器。对于安全过滤器,您可以按名称将其注入注册 bean。您可能可以通过调用@Bean 方法注入其他人。

        【讨论】:

        • 非常感谢@Dave Syer。我仍然缺少的是为什么现在没有两个过滤器链,我的意思是框架如何意识到我添加了一个 FilterRegistration Bean(包装过滤器链)以便过滤器链它自己不会被添加为过滤器,我会很高兴你能详细说明一下。
        • 查看EmbeddedWebApplicationContext 中的代码,它分析了FilterRegistrationBeans。我们不会重复注册已经是注册一部分的过滤器。
        猜你喜欢
        • 1970-01-01
        • 2019-12-25
        • 2012-10-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-23
        • 2014-07-28
        • 2014-09-04
        相关资源
        最近更新 更多