0

我试图在我的应用程序中添加 CSRF/XSRF 保护,但遇到了奇怪的行为。所有获取请求都可以正常工作,但是在所有发布/放置/删除时,我都会收到 403 Unauthorized。最奇怪的是,当我尝试调试我的 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;
        }

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

4

3 回答 3

4

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

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

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

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

于 2017-09-09T14:25:24.397 回答
1

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

正如我使用SessionCreationPolicy.STATELESS并且没有会话的答案中所述,因此HttpSessionCsrfTokenRepository我不得不使用CookieCsrfTokenRepositorywithwithHttpOnlyFalse()来允许 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');
}]);

我希望我的回答会有用。

于 2017-09-09T21:47:44.653 回答
1

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

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

接下来要做什么 - 真的取决于你。有人说如果你的应用是无状态的,实际上就不需要 CSRF 保护。Spring 文档说 CSRF 攻击仍然相关。我认为这实际上取决于您的身份验证机制。

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

希望能帮助到你。

于 2017-09-09T10:13:27.540 回答