Angularjs CSRF/XSRF保护弹簧安全性和角度

Angularjs CSRF/XSRF保护弹簧安全性和角度,angularjs,spring,spring-security,csrf,x-xsrf-token,Angularjs,Spring,Spring Security,Csrf,X Xsrf Token,我试图将CSRF/XSRF保护添加到我的应用程序中,但遇到了奇怪的行为。所有get请求都可以正常工作,但是在所有post/put/delete上,我得到了403个未经授权的请求。最奇怪的是,当我试着调试我的CSRF过滤器时,请求没有到达它,它们在早些时候被拒绝了。它们甚至没有到达我的身份验证过滤器,因此我无法找出问题所在 我的安全配置: @Override public void configure(HttpSecurity http) throws Except

我试图将CSRF/XSRF保护添加到我的应用程序中,但遇到了奇怪的行为。所有get请求都可以正常工作,但是在所有post/put/delete上,我得到了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;
        }

我没有添加过滤器,因为正如我所说,请求没有到达它们。但如果需要,我会完成我的问题。希望您的帮助,提前谢谢

假设其余的配置/过滤器工作正常,您将面临这个问题,因为:
SessionCreationPolicy.STATELESS

您可以在弹簧罩下查看
CsrfFilter
。您将看到,它需要记住会话中每个用户的每个CSRF令牌的值,并且由于您没有使用会话,因此无法执行此操作

下一步该怎么办?这完全取决于你。有些人说,如果你的应用程序是无状态的,实际上就不需要CSRF保护。说CSRF攻击仍然相关。我认为这取决于你的认证机制

例如,您可能还想看看


希望能有所帮助。

假设您的其他配置/过滤器工作正常,您将因此而面临此问题:
SessionCreationPolicy.STATELESS

您可以在弹簧罩下查看
CsrfFilter
。您将看到,它需要记住会话中每个用户的每个CSRF令牌的值,并且由于您没有使用会话,因此无法执行此操作

下一步该怎么办?这完全取决于你。有些人说,如果你的应用程序是无状态的,实际上就不需要CSRF保护。说CSRF攻击仍然相关。我认为这取决于你的认证机制

例如,您可能还想看看


希望有帮助。

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

.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
然后,您可以从Angular读取cookie,并随每个请求将其添加到XSRF-TOKEN头中


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

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

.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
然后,您可以从Angular读取cookie,并随每个请求将其添加到XSRF-TOKEN头中


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

非常感谢您的回答,他们真的帮助我找到了解决方案。如果将来有人会面临同样的问题,我想分享我的解决方案

如回答中所述,我使用了
SessionCreationPolicy.STATELESS
并且没有会话,因此我必须使用
CookiesRftokenRepository
和httponlyFalse()
来代替
HttpSessionsRftokenRepository,以允许AngularJS读取cookies

因此,我的配置如下:

@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');
}]);

我希望我的答案会有用。

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

如回答中所述,我使用了
SessionCreationPolicy.STATELESS
并且没有会话,因此我必须使用
CookiesRftokenRepository
和httponlyFalse()
来代替
HttpSessionsRftokenRepository,以允许AngularJS读取cookies

因此,我的配置如下:

@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');
}]);

我希望我的答案会有用。

因此,我实现了定制的CSRF过滤器,但我的问题是,请求甚至无法到达它。这很奇怪。@AnarSultanov,你能分享一下你的过滤器代码吗?尽管如此,这并不重要,因为您在Spring的
CsrfFilter
之后添加了过滤器。如果Springs筛选器无法匹配来自用户会话的令牌(由于没有会话,它无法获取令牌),它将调用
accessDeniedHandler
,并执行
return
(断开筛选器链),这意味着它没有调用
filterChain.doFilter(请求,响应),因此您的筛选器实际上无法在之后调用。如果您正在实现自己的CSRF保护,那么,我想,您应该通过调用
.CSRF().disable()
来禁用Spring的实现。我发现了我的问题所在。我在Cookie中有XSRF-TOKEN,但不会将带有此令牌的头添加到请求中。现在我正试图找出原因以及如何解决这个问题。出于这个原因,我实现了我的定制CSRF过滤器,但我的问题是,请求甚至无法到达它。这很奇怪。@AnarSultanov,你能分享一下你的过滤器代码吗?尽管如此,这并不重要,因为您在Spring的
CsrfFilter
之后添加了过滤器。如果Springs筛选器无法匹配来自用户会话的令牌(由于没有会话,它无法获取令牌),它将调用
accessDeniedHandler
,并执行
return
(断开筛选器链),这意味着它没有调用
filterChain.doFilter(请求,响应),因此您的筛选器无法实际运行