繁体   English   中英

从 Angular 2 向 Spring-backed Java 应用程序发送 POST 请求时出现 CSRF 问题

[英]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。

更新角度 >= 6

@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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM