如何仅针对特定 url 添加 Spring Security 验证码过滤器

Posted

技术标签:

【中文标题】如何仅针对特定 url 添加 Spring Security 验证码过滤器【英文标题】:Howto additionally add Spring Security captcha filter for specific urls only 【发布时间】:2016-03-22 17:03:56 【问题描述】:

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

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

内部 api(“/iapi”在所有调用中使用过滤器 A,但也会忽略一些 public 请求,例如 /authenticate) 外部 api(“/eapi”在所有调用中使用过滤器 B)

如何在公共、内部 api 或外部 api 调用中添加过滤器之前 Spring Security 的东西?我不需要SecurityContext,只需要检查请求标头中的验证码,转发到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 的解决方案。

总结:还有两个问题:

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

【问题讨论】:

您说的是Filter AFilter B。它们是您的验证码过滤器的占位符,还是您有真正的实现?如果是这样,您能否相应地更新您的问题? 不确定它是否正是您要找的东西,但发现this answer 有一个类似的问题。不同之处在于它们在链的末尾添加了过滤器,并且配置是 XML 格式的,但是在 javaconfig 中,http.addFilterBefore() 也可以解决问题。 @ksokol 它们是当前系统的过滤器,我不想碰它们。 @SalvadorJuanMartinez 我认为它不会起作用。这是一个 xml 配置,我的主要问题是,我只想在不同配置中的特定路径之前添加一个过滤器 - 所以我不需要更改我的整个设置。 对不起,我想我没有解释清楚,我不是说要更改 XML 配置和/或在末尾添加过滤器,而是可以用作参考,因为它是如何向链中添加自定义过滤器的工作示例。它提供了有关如何正确实现过滤器的信息。在您的情况下,您可以坚持.addFilterBefore() 将过滤器插入您想要的位置。 【参考方案1】:

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

首先,您创建过滤器,并将其声明为 bean,或者使用 @Component 注释该类,或者作为 @Configuration 类中的 @Bean,以便可以使用 @Autowired 注入它.

现在您可以将它作为过滤器 A 和 B 注入并使用它。根据 Spring Security 参考文档中的 Filter Ordering 部分,链中的第一个过滤器是 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();
    

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

请记住,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方法声明的东西。

虽然WebSecurity 会有一个问题,但如果您忽略/public/**,则HttpSecurity/public/captcha** 的匹配器将被忽略,所以我想,您不应该重构WebSecurity 和CaptchaConfig 类中的模式不同,因此不会重叠。

【讨论】:

对不起,我已经尝试过这种方法。它会在任何请求上使用这两个过滤器,但我需要特定的路径。另外我仍然不知道如何在非 Spring Security 调用中添加它们。我相应地更新了我的问题。感谢有关异常处理的提示,这些东西有助于我更好地理解 Spring Security! 没有什么可以阻止您为验证码过滤器添加第三个配置,请记住@Order,我将通过示例更新答案。

以上是关于如何仅针对特定 url 添加 Spring Security 验证码过滤器的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 sec:authorize with thymeleaf 向特定用户授权 html 元素?

Spring security:在 3.1 中,仅针对“GET”请求绕过安全过滤器

仅针对 nginx 上的特定 url 使用 https 重定向

仅针对特定URL的Tomcat基本认证

spring security - 针对不同 CURL 模式的多重身份验证

仅针对特定文件类型将菜单项添加到 Windows 上下文菜单