如何仅针对特定网址另外添加 Spring 安全验证码过滤器

Howto additionally add Spring Security captcha filter for specific urls only

我正在寻找一种非侵入性的方法来为某些 api 调用添加验证码过滤器。

我的设置包含两个 WebSecurityConfigurerAdapters,每个都有一个过滤器(不是验证码过滤器):

如何在 Spring 安全性内容 之前 添加过滤器 public、内部 api 或外部 api 电话?我不需要 SecurityContext,只需要在请求 headers 中检查验证码,转发到 filterChain(普通过滤器)或手动拒绝访问。我尝试在 web.xml 中声明一个过滤器,但这破坏了使用依赖注入的能力。

这是我的 Spring 安全配置:

@EnableWebSecurity
public class SpringSecurityConfig {
    @Configuration
    @Order(1)
    @EnableGlobalMethodSecurity(securedEnabled = true)
    public static class InternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Autowired
        private Filter filterA;

        public InternalApiConfigurerAdapter() {
            super(true);
        }

        @Override
        public void configure(WebSecurity web) throws Exception {
            web
                    .ignoring()
                    .antMatchers("/public/**");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .antMatcher("/iapi/**")
                    .exceptionHandling().and()
                    .anonymous().and()
                    .servletApi().and()
                    .authorizeRequests()
                    .anyRequest().authenticated().and()
                    .addFilterBefore(filterA, (Class<? extends Filter>) UsernamePasswordAuthenticationFilter.class);
        }

        @Override
        @Bean
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return authenticationManager();
        }
    }

    @Configuration
    @Order(2)
    @EnableGlobalMethodSecurity(securedEnabled = true)
    public static class ExternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Autowired
        private FilterB filterB;

        public ExternalApiConfigurerAdapter() {
            super(true);
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .antMatcher("/external/**")
                    .exceptionHandling().and()
                    .anonymous().and()
                    .servletApi().and()
                    .authorizeRequests()
                    .anyRequest().authenticated().and()
                    .addFilterBefore(filterB, (Class<? extends Filter>) UsernamePasswordAuthenticationFilter.class);
        }

        @Override
        @Bean
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return authenticationManager();
        }
    }

更新: 目前我有一个工作配置,其中包含在 web.xml 中声明的过滤器。但是,它的缺点是与 Spring 上下文分离(例如,没有 bean 的自动装配),因此我正在寻找利用 Spring.

的更好解决方案

总结:还有两个问题:

  1. 仅为特定网址添加过滤器 - 在任何配置中使用 beforeFilter(...) 会向该配置的所有网址添加过滤器。 Antmatchers 没有工作。我需要这样的东西:/iapi/captcha/、/external/captcha/、/public/captcha/*.
  2. 我有一个 public api 完全绕过了 Spring 安全:(web .忽略() .antMatchers("/public/**");。我需要绕过 Spring 安全,但仍然在那里声明一个过滤器,使用 Spring 自动装配但不一定 Spring 安全功能,因为我的验证码过滤器只以无状态的方式拒绝或转发呼叫。

您已经有一个工作配置,在 UsernamePasswordAuthenticationFilter 之前插入了过滤器 A 和 B,因此添加另一个自定义过滤器应该很容易。

首先,您创建过滤器,并将其声明为一个 bean,或者用 @Component 注释 class,或者作为 @Configuration 中的 @Bean class,所以准备注入@Autowired.

现在您可以将其作为过滤器 A 和 B 注入并使用。根据 Spring 安全参考文档中的 Filter Ordering 部分,链中的第一个过滤器是 ChannelProcessingFilter,因此为了在 [=64= 中的任何其他内容之前插入过滤器] 安全过滤器链,你会这样做:

@Autowired
private CaptchaFilter captchaFilter;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .antMatcher("/iapi/**")
            .addFilterBefore(captchaFilter, (Class<? extends Filter>) ChannelProcessingFilter.class)
            .addFilterBefore(filterA, (Class<? extends Filter>) UsernamePasswordAuthenticationFilter.class)
            .authorizeRequests()
                .anyRequest().authenticated();
    }

顺便说一句,不需要 exceptionHandling() anonymous()servletApi(),因为在扩展 WebSecurityConfigurerAdapter 时,它们已经包括在内,除了 anonymous()当您实际指定更多配置详细信息时,如它所述 HttpSecurity javadoc

请记住 Spring 安全性 "entrypoint",DelegatingFilterProxy 仍将在您的过滤器之前执行,但此组件仅将请求委托给链中的第一个过滤器,在本例中是 CaptchaFilter,所以你真的会在 Spring Security.

的任何其他事情之前执行你的过滤器

但是如果你还想在DelegatingFilterProxy之前执行验证码过滤,在Spring安全配置中是没有办法的,需要在[=]中声明26=] 文件.


更新:如果你不想在其他配置中包含验证码过滤器,你可以随时添加第三个配置,配置class如下:

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SpringSecurityConfig {

    @Configuration
    @Order(1)
    public static class CaptchaApiConfigurerAdatper extends WebSecurityConfigurerAdapter {

        @Autowired
        private CaptchaFilter captchaFilter;

        public CaptchaApiConfigurerAdatper() {
            super(true);
        }

        @Override
        public void configure(WebSecurity web) throws Exception {
            web
                    .ignoring()
                    .antMatchers("/public/**");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .requestMatchers()
                        .antMatcher("/iapi/captcha**")
                        .antMatcher("/external/captcha**")
                        .and()
                    .addFilterBefore(captchaFilter, (Class<? extends Filter>) ChannelProcessingFilter.class)
                    .authorizeRequests()
                        .anyRequest().authenticated();
        }
    }            

    @Configuration
    @Order(2)
    public static class InternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {

        // ommiting code for the sake of clarity
    }

    @Configuration
    @Order(3)
    public static class ExternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {

         // ommiting code for the sake of clarity
    }

对了,还有个小技巧,你可以重构所有在特定配置之外的通用配置,进入mainclass,像@EnableGlobalMethodSecurity(securedEnabled = true)AuthenticationManager,WebSecurity跳过public 的安全性,但对于那些因为主要 class 没有扩展任何东西你应该 @Autowire 方法声明。

虽然 WebSecurity 会有一个问题,但如果您忽略 /public/**HttpSecurity/public/captcha** 的匹配器将被忽略,所以我猜,您不应该重构 WebSecurity 并在 CaptchaConfig class 中使用不同的模式,这样它就不会重叠。