[英]CSRF issue while sending POST request from Angular 2 to Spring-backed Java app
我有使用Angular 2和基于 Java 的后端编写的 UI,它在Spring Security之上使用 OpenID Connect 身份验证。
身份验证工作正常,但仅适用于 GET 请求。 每次对资源执行 POST、PUT 或 DELETE 方法时,我都会收到 HTTP 403:
{
"status": 403,
"message": "Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.",
}
我像这样使用 HttpClient:
http.post(
'/api/my-resource',
JSON.stringify(myObject),
new RequestOptions({
headers: new Headers({'Content-Type': 'application/json'})
})
)
当我将withCredentials: true
添加到这里建议的 RequestOptions 时,我仍然收到 HTTP 403,但有不同的消息:
{
"status": 403,
"message": "Could not verify the provided CSRF token because your session was not found.",
}
我需要做什么才能使HttpClient
与 CSRF 一起工作?
注意:我还查看了类似的问题,尤其是angular-2-spring-security-csrf-implementation-problems ,但那里提出的解决方案并没有解决我的问题。 同时我不想禁用CSRF。
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,
HttpClientXsrfModule.withOptions({cookieName: 'XSRF-TOKEN'})
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}
添加CSRF
cookie配置如下:
@NgModule({
declarations: [declarations],
imports: [imports],
providers:
[
{provide: XSRFStrategy, useFactory: xsrfFactory}
],
bootstrap: [AppComponent]
})
export class AppModule {
}
export function xsrfFactory() {
return new CookieXSRFStrategy('XSRF-TOKEN', 'XSRF-TOKEN');
}
并使用正确的标头名称配置 Spring 安全性。
private static final String CSRF_HEADER_NAME = "XSRF-TOKEN";
@Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.and()
.csrf()
.ignoringAntMatchers(CSRF_IGNORE)
.csrfTokenRepository(csrfTokenRepository())
.and()
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName(CSRF_HEADER_NAME);
return repository;
}
其中CsrfHeaderFilter :
public class CsrfHeaderFilter extends OncePerRequestFilter {
private static final String CSRF_COOKIE_NAME = "XSRF-TOKEN";
@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, CSRF_COOKIE_NAME);
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie(CSRF_COOKIE_NAME, token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}
使用 spring-boot 2.2.2 (spring-security 5.2.1) 和 Angular 8.2.14,这个配置对我有用:
@NgModule({
declarations: [],
imports: [
BrowserModule,
HttpClientModule,
HttpClientXsrfModule.withOptions({cookieName: 'XSRF-TOKEN'}),
与 Radouane 的更新响应相同的配置。
但是对于 springboot 部分:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.csrfTokenRepository(cookieCsrfTokenRepository())
.and()
.authorizeRequests()
.antMatchers("/static/**").permitAll()
...
.anyRequest().authenticated();
}
private CookieCsrfTokenRepository cookieCsrfTokenRepository() {
final CookieCsrfTokenRepository cookieCsrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
cookieCsrfTokenRepository.setCookiePath("/");
return cookieCsrfTokenRepository;
}
根据这个答案,
仅当 XSRF-TOKEN cookie 是在服务器端使用以下选项生成时,Angular 才会添加 X-XSRF-TOKEN 标头:
Path = /
httpOnly = false
此外,Angular 应用程序和被调用的 URL 必须位于同一台服务器上。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.