注意:我正在使用 CORS 和 AngularJS。
注意²:我发现 Stateless Spring Security Part 1: Stateless CSRF protection 保留 AngularJS 处理 CSRF 的方式会很有趣。
我没有使用基于答案的Spring Security CSRF Filter(尤其是@Rob Winch 的答案),而是使用The Login Page: Angular JS and Spring Security Part II 中描述的方法。
除此之外,我还必须添加 Access-Control-Allow-Headers: ..., X-CSRF-TOKEN(由于 CORS)。
实际上,我发现这种方法比在响应中添加标头更干净。
代码如下:
HttpHeaderFilter.java
@Component("httpHeaderFilter")
public class HttpHeaderFilter extends OncePerRequestFilter {
@Autowired
private List<HttpHeaderProvider> providerList;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
providerList.forEach(e -> e.filter(request, response));
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
response.setStatus(HttpStatus.OK.value());
}
else {
filterChain.doFilter(request, response);
}
}
}
HttpHeaderProvider.java
public interface HttpHeaderProvider {
void filter(HttpServletRequest request, HttpServletResponse response);
}
CsrfHttpHeaderProvider.java
@Component
public class CsrfHttpHeaderProvider implements HttpHeaderProvider {
@Override
public void filter(HttpServletRequest request, HttpServletResponse response) {
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "X-CSRF-TOKEN");
}
}
CsrfTokenFilter.java
@Component("csrfTokenFilter")
public class CsrfTokenFilter 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);
}
}
web.xml
...
<filter>
<filter-name>httpHeaderFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>httpHeaderFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
security-context.xml
...
<custom-filter ref="csrfTokenFilter" after="CSRF_FILTER"/>
...
app.js
...
.run(['$http', '$cookies', function ($http, $cookies) {
$http.defaults.transformResponse.unshift(function (data, headers) {
var csrfToken = $cookies['XSRF-TOKEN'];
if (!!csrfToken) {
$http.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken;
}
return data;
});
}]);