如何为特定网址额外添加弹簧安全验证码过滤器

2022-09-04 03:08:16

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

我的设置由两个组成,每个过滤器有一个过滤器(不是验证码过滤器):WebSecurityConfigurerAdapters

  • 内部 API(“/iapi”在所有调用上使用筛选器 A,但也忽略一些公共请求,如 /authenticate)
  • 外部 api(“/eapi”在所有调用中使用筛选器 B)

如何在Spring Security之前,在公共,内部API或外部API调用添加过滤器?我不需要SecurityContext,只需要检查请求标头中的验证码,转发到filterChain(普通过滤器)或手动拒绝访问。我尝试在web.xml中声明过滤器,但这破坏了使用依赖关系注入的能力。

这是我的弹簧安全配置:

@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 Context分开的缺点(例如,没有豆子的自动布线),因此我正在寻找利用Spring的更好解决方案。

摘要:还有两个剩余问题:

  1. 仅为特定 URL 添加筛选器 - 在任何配置中使用 beforeFilter(...) 将筛选器添加到该配置的所有 URL。Antmatchers不起作用。我需要这样的东西:/iapi/captcha/,/external/captcha/,/public/captcha/*。
  2. 我有一个完全绕过Spring Security的公共API:(web .ignoring() .antMatchers(“/public/**”);)。我需要绕过Spring Security,但仍然使用Spring自动布线,但不一定是Spring Security功能,因为我的验证码过滤器仅以无状态方式拒绝或转发呼叫。

答案 1

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

首先,创建过滤器,并将其声明为 Bean,要么使用 对类进行注释,要么将其作为类内部的注释,以便可以使用 。@Component@Bean@Configuration@Autowired

现在,您可以将其作为过滤器A和B注入并使用它。根据Spring Security参考文档中的过滤器排序部分,链中的第一个过滤器是,因此,为了在Spring Security过滤器链中的其他任何内容之前插入过滤器,您需要执行以下操作:ChannelProcessingFilter

@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();
    }

顺便说一句,并且不需要,因为在扩展时,这些已经包含在内,除非您实际指定了更多配置详细信息,因为它声明HttpSecurity javadocexceptionHandling()anonymous()servletApi()WebSecurityConfigurerAdapteranonymous()

请记住,Spring Security的“入口点”,静止图像将在您的过滤器之前执行,但是此组件仅将请求委托给链中的第一个过滤器,在这种情况下,该过滤器将是CaptchaFilter,因此您实际上会在Spring Security的任何其他操作之前执行过滤器。DelegatingFilterProxy

但是,如果您仍然希望在 之前执行验证码过滤器,则在Spring Security配置中没有办法这样做,您需要在文件中声明它。DelegatingFilterProxyweb.xml


更新:如果您不希望在其他配置中包含验证码过滤器,则始终可以添加第三个配置,配置类将如下所示:

@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
    }

顺便说一句,另一个提示,您可以将特定配置之外的所有常见配置重构为主类,如Sauthormanager,为公众跳过安全性,但是对于那些由于主类没有扩展任何内容,您应该对方法声明。@EnableGlobalMethodSecurity(securedEnabled = true)WebSecurity@Autowire

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


答案 2

推荐