如何为特定的URL添加Spring Security验证码过滤器

14

我正在寻找一种非侵入式的方法,为特定的 API 调用添加验证码过滤器。

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

  • 内部 API("/iapi" 对所有调用使用过滤器 A,但也忽略一些公共请求,如 /authenticate)
  • 外部 API("/eapi" 对所有调用使用过滤器 B)

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

这是我的 Spring Security 配置:

@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. 我有一个公共API完全绕过Spring Security:(web .ignoring() .antMatchers("/public/**");)。我需要绕过Spring Security,但仍然在那里声明一个过滤器,使用Spring自动连接,但不一定是Spring Security功能,因为我的验证码过滤器仅以无状态方式拒绝或转发调用。

1
你提到了 Filter AFilter B。它们是你验证码过滤器的占位符,还是你已经有了真正的实现?如果是这样,请相应地更新你的问题。 - ksokol
1
不太确定这是否正是您要寻找的,但发现了一个类似问题的答案此答案链接。不同之处在于它们在链的结尾添加了过滤器,并且配置是以XML形式提供的,但在Javaconfig中,http.addFilterBefore()也可以做到这一点。 - saljuama
@ksokol 这些是当前系统的过滤器,我不想动它们。 - Journeycorner
@SalvadorJuanMartinez 我觉得这不会起作用。它是一个 XML 配置文件,我的主要问题是,我想在不同的配置中仅在特定路径前添加一个过滤器 - 这样我就不需要改变整个设置。 - Journeycorner
抱歉,我想我没有表达清楚,我的意思不是改变XML配置或在结尾添加过滤器,而是可以作为参考使用,因为这是一个如何向链中添加自定义过滤器的工作示例。它提供了正确实现过滤器的信息。在你的情况下,你可以使用.addFilterBefore()将过滤器插入到想要的位置。 - saljuama
1个回答

14

您已经有一个配置好的过滤器 A 和 B,它们插入了 UsernamePasswordAuthenticationFilter,所以添加另一个自定义过滤器应该很容易。

首先,您创建过滤器,并将其声明为 bean,可以使用 @Component 对类进行注释,也可以在 @Configuration 类内部使用 @Bean 进行声明,以便可以使用 @Autowired 进行注入。

现在,您可以像过滤器 A 和 B 一样注入并使用它。根据 Spring Security 参考文档中的过滤器排序部分,链中的第一个过滤器是 ChannelProcessingFilter,因此要将过滤器插入到 Spring Security 过滤器链中的其他内容之前,您应该这样做:

@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();
    }
顺便提一下,当扩展WebSecurityConfigurerAdapter时,exceptionHandling()anonymous()servletApi()是不需要的,因为它们已经包含在内了,除非你实际指定了更多的配置详细信息,如HttpSecurity javadoc所述。
请记住,Spring Security的“入口点”DelegatingFilterProxy仍将在您的过滤器之前执行,但是该组件只会将请求委托给链中的第一个过滤器,在这种情况下,它将是CaptchaFilter,因此您确实会在Spring Security的任何其他内容之前执行自己的过滤器。
但是,如果您仍希望在DelegatingFilterProxy之前执行验证码过滤器,则无法在Spring Security配置中这样做,您需要在web.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
    }

顺便提一下,另一个提示是,您可以将所有通用配置从特定配置中移出到主类中进行重构,例如像@EnableGlobalMethodSecurity(securedEnabled = true)的身份验证管理器 AuthenticationManager,以及WebSecurity为公共安全跳过,但对于那些由于主类未扩展任何内容的其他情况,您应该使用@Autowire方法声明。

不过,这样做会有一个问题,如果您忽略了/public/** ,则匹配HttpSecurity/public/captcha**,所以我猜您不应该重构WebSecurity,而应该在CaptchaConfig类中使用不同的模式以避免重叠。


抱歉,我已经尝试过那种方法了。它会在任何请求上使用这两个过滤器,但我需要特定的路径。此外,我仍然不知道如何在非Spring Security调用中添加它们。我相应地更新了我的问题。感谢有关异常处理的提示,这些东西帮助我更好地理解Spring Security! - Journeycorner
你可以随意添加第三个验证码过滤器的配置,只需记住@Order即可。我会在答案中更新一个示例。 - saljuama

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接