2017-05-28 178 views
0

我有一个现有的Spring WebMVC应用程序,并启用了spring security。现在我想启用Spring-Security CSRF检查。Spring Security CSRF培训模式

但我想让它在训练模式下运行。所以CSRF-Filter应该只记录任何丢失的CSRF-Token,以便它可以不时地实施。

在CSRF处理的Spring-Security中是否有这样的培训模式?

我试图实现自定义org.springframework.security.web.access.AccessDeniedHandler,并将其设置在WebSecurityConfigurerAdapter

http.exceptionHandling().accessDeniedHandler(new MyCustomAccessDeniedHandler()); 

但遗憾的是该请求不suceed因为过滤器链停止。

我想我必须实现一个自定义CsrfFilter,但我该如何设置它?

有没有一种简单的方法来实施培训模式? 任何建议或指针在正确的方向?

回答

1

由于CsrfFilterfinal,覆盖或包装它几乎是不可能的。

但是,可能有几种选择来实现某种训练模式 - 所有这些都有一定的缺点和可能的安全问题。所以要小心。

这里是一个可能的解决方案:

  1. 提供了一个自定义CsrfFilter#accessDeniedHandlerAccessDeniedHandler,通过抛出一个自定义异常
  2. 使用自定义筛选权之前CsrfFilter处理处理任何丢失/错误的记号/日志自定义异常并继续链

下面是它可能是什么样子:

class MissingCsrfTokenException extends RuntimeException { 
} 

/** 
* Post process the CsrfFilter to use a custom AccessDeniedHandler 
*/ 
@Component 
class CsrfFilterBeanPostProcessor implements BeanPostProcessor { 

    @Override 
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
     if (bean instanceof CsrfFilter) { 
      CsrfFilter csrfFilter = (CsrfFilter) bean; 
      csrfFilter.setAccessDeniedHandler(new AccessDeniedHandler() { 

       /** 
       * Default CsrfFilter AccessDeniedHandler implementation (org.springframework.security.web.access.AccessDeniedHandler) 
       */ 
       AccessDeniedHandler delegate = new AccessDeniedHandlerImpl(); 

       @Override 
       public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { 

        // test whether the request requires a valid CsrfToken (or is in training mode) 
        boolean inTrainingMode = ... 
        if (inTrainingMode) { 
         throw new MissingCsrfTokenException(); 
        } else { 
         // delegate to the default AccessDeniedHandler 
         delegate.handle(request, response, accessDeniedException); 
        } 
       } 
      }); 
     } 
     return bean; 
    } 

    @Override 
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
     return bean; 
    } 
} 

/** 
* Custom Filter that logs the token-exceptions and continues the chain. 
*/ 
class PreCsrfFilterFilter extends OncePerRequestFilter { 

    private Logger logger = LoggerFactory.getLogger(PreCsrfFilterFilter.class); 

    @Override 
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 
     try { 
      filterChain.doFilter(request, response); 
     } catch (MissingCsrfTokenException ex) { 
      // log and continue the filter chain 
      logger.warn("No CSRF-Token found for {}", request.getRequestURI()); 
      filterChain.doFilter(request, response); 
     } 
    } 
} 

@Configuration 
@EnableWebSecurity 
class Config extends WebSecurityConfigurerAdapter { 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http 
       .authorizeRequests() 
       .anyRequest().authenticated() 
       .and() 
       .formLogin().and() 
       .httpBasic() 
       .and() 
       // add the custom filter before the CsrfFilter 
       .addFilterBefore(new PreCsrfFilterFilter(), CsrfFilter.class); 
    } 
} 
+0

非常感谢!这个解决方案适用于我。我特别喜欢在过滤器链中捕捉异常的“技巧”。也感谢你指点我的BeanPostProcessor。我没有意识到这一点。在我看来,仅仅为了'CsrfFilter'而改变'AccessDeniedHandler'的做法比在我尝试做的时候更全面。 – Tarator

+0

感谢您的反馈!很高兴我能帮上忙! – fateddy