如何仅为特定网址

时间:2015-12-16 14:03:23

标签: java spring spring-security captcha

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

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

  • 内部api (“/ iapi”在所有来电中使用过滤器A,但也会忽略某些公开请求,例如/ authenticate)
  • 外部api (“/ eapi”在所有来电时使用过滤器B)

如何在公共,内部api或外部api调用上添加过滤器之前之前的内容?我不需要SecurityContext,只需要检查请求头中的Captcha,转发到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 Context分离(例如没有bean的自动装配),因此我正在寻找一种利用Spring的更好的解决方案。

摘要:还有两个问题:

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

1 个答案:

答案 0 :(得分:6)

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

首先,创建过滤器,并将其声明为bean,使用@Component注释该类,或者在@Bean类中注释为@Configuration,因此它已准备好注入@Autowired

现在您可以将其作为过滤器A和B注入,并使用它。根据Spring Security参考文档中的Filter Ordering部分,链中的第一个Filter是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()除外实际上指定了更多配置详细信息,因为它指出了HttpSecurity javadoc

请记住Spring Security“入口点”,DelegatingFilterProxy仍然会在过滤器之前执行,但是这个组件只将请求委托给链中的第一个过滤器,在这种情况下,它将是CaptchaFilter,所以你真的会在Spring Security之前执行你的过滤器。

但是如果您仍然希望在DelegatingFilterProxy之前执行验证码过滤器,则无法在Spring Security配置中执行此操作,您需要在web.xml文件中声明它。 / p>

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

@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类中使用不同的模式,因此它不会重叠。