我尝试将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;
}
我没有添加过滤器,因为正如我所说,请求没有到达它们。但如果需要,我会完成我的问题。我希望得到你的帮助,提前谢谢你!
答案 0 :(得分:0)
假设您的配置/过滤器的其余部分正常运行,您就会因此而遇到此问题:SessionCreationPolicy.STATELESS
。
你可以看看Spring CsrfFilter
的内幕。您将看到它需要记住会话中每个用户的每个CSRF令牌的值,并且由于您没有使用会话,因此无法完成。
下一步该做什么 - 真的取决于你。有人说,如果你的应用程序是无状态的,实际上不需要CSRF保护。 Spring docs说CSRF攻击仍然具有相关性。我认为这实际上取决于您的身份验证机制。
您可能还想查看this nice article,例如。
希望它有所帮助。
答案 1 :(得分:0)
原则上,Spring中的CSRF机制将CSRF令牌存储在仅HTTP的cookie中。由于JavaScript无法访问仅HTTP的cookie,因此您需要告诉spring仅禁用HTTP:
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
然后,您可以从Angular中读取cookie,并将每个请求添加到XSRF-TOKEN标头中。
这是一般情况。我不确定这是否适合您的特殊情况。
答案 2 :(得分:0)
非常感谢答案,他们真的帮助我找到了解决方案。如果将来有人会面临同样的问题,我想分享我的解决方案。
正如答案中所述,我使用了SessionCreationPolicy.STATELESS
并且没有会话,因此我不得不使用HttpSessionCsrfTokenRepository
CookieCsrfTokenRepository
而不是withHttpOnlyFalse()
来允许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');
}]);
我希望我的答案有用。