繁体   English   中英

用于Spring Security和AngularJS的CSRF / XSRF保护

[英]CSRF/XSRF protection for Spring Security and AngularJS

我试图将CSRF / XSRF保护添加到我的应用程序中,但是遇到了奇怪的行为。 所有的get请求都可以正常工作,但是在所有发布/发布/删除操作中,我都得到403未经授权。 最奇怪的是,当我尝试调试CSRF过滤器时,请求没有到达,它们会在更早的某个地方被拒绝。 它们甚至没有到达我的身份验证筛选器,因此我无法弄清楚可能是什么问题。

我的安全配置:

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                    ...
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService()), UsernamePasswordAuthenticationFilter.class)
                    .addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
                    .csrf().csrfTokenRepository(csrfTokenRepository());
        }

        private CsrfTokenRepository csrfTokenRepository() {
            HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
            repository.setHeaderName("X-XSRF-TOKEN");
            return repository;
        }

我没有添加过滤器,因为正如我所说,请求没有到达它们。 但是,如果需要,我将完成我的问题。 希望对您有所帮助,在此先感谢您!

原则上,Spring中的CSRF机制将CSRF令牌存储在仅HTTP cookie中。 因为JavaScript无法访问仅HTTP的cookie,所以您需要告诉spring仅禁用HTTP:

.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

然后,您可以从Angular中读取cookie,并将其与每个请求一起添加到XSRF-TOKEN标头中。

这是一般情况。 我不确定这是否适合您的特殊情况。

假设其余配置/过滤器正常工作,则您将面临以下问题: SessionCreationPolicy.STATELESS

您可以在Spring CsrfFilter 您将看到它需要记住会话中每个用户的每个CSRF令牌的值,并且由于您没有使用会话,因此无法完成。

下一步该怎么做-完全取决于您。 有人说,如果您的应用程序是无状态的,则实际上不需要CSRF保护。 Spring的文档说CSRF攻击仍然很重要。 我认为这确实取决于您的身份验证机制。

例如,您可能还想看看这篇不错的文章

希望能帮助到你。

非常感谢您的回答,它们确实帮助我找到了解决方案。 如果将来有人遇到同样的问题,我想分享我的解决方案。

如答案中所述,我使用SessionCreationPolicy.STATELESS且没有会话,因此我不得不使用CookieCsrfTokenRepositorywithHttpOnlyFalse()来代替HttpSessionCsrfTokenRepository ,以允许AngularJS读取cookie。

结果,我有这样的配置:

@Override
public void configure(HttpSecurity http) throws Exception {
    http
            ...
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService()), UsernamePasswordAuthenticationFilter.class)
            .addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
            .csrf().csrfTokenRepository(csrfTokenRepository());
}

如果有人对CsrfHeaderFilter的外观感兴趣:

public class CsrfHeaderFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
        if (csrf != null) {
            Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
            String token = csrf.getToken();
            if (cookie==null || token!=null && !token.equals(cookie.getValue())) {
                cookie = new Cookie("XSRF-TOKEN", token);
                cookie.setPath("/");
                response.addCookie(cookie);
            }
        }
        filterChain.doFilter(request, response);
    }
}

我的第二个问题是CORS。 AngularJS文档说:

“不会为跨域请求设置标题。”

要解决此问题,我必须使用HTTP拦截器:

.factory('XsrfInterceptor', function ($cookies) {
  return {
    request: function (config) {
      var headerName = 'X-XSRF-TOKEN';
      var cookieName = 'XSRF-TOKEN';
      config.headers[headerName] = $cookies.get(cookieName);
      return config;
    }
  };
});

.config(['$httpProvider', function($httpProvider) {  
    $httpProvider.interceptors.push('XsrfInterceptor');
}]);

希望我的回答会有用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM