Angular 6 不会将 X-XSRF-TOKEN 标头添加到 http 请求

Posted

技术标签:

【中文标题】Angular 6 不会将 X-XSRF-TOKEN 标头添加到 http 请求【英文标题】:Angular 6 does not add X-XSRF-TOKEN header to http request 【发布时间】:2018-11-03 18:41:38 【问题描述】:

我已经阅读了the docs 以及关于 SO 的所有相关问题,但 Angular 的 XSRF 机制仍然对我不起作用:我无法发出自动附加 X-XSRF-TOKEN 标头的 POST 请求。

我有一个带有登录表单的 Angular 6 应用程序。

它是 Symfony (php 7.1) 网站的一部分,当从 Symfony 提供时,Angular 应用页面会发送正确的 Cookie (XSRF-TOKEN):

我的 app.module.ts 包含正确的模块:

// other imports...
import HttpClientModule, HttpClientXsrfModule from "@angular/common/http";

// ...
@NgModule(
  declarations: [
    // ...
  ],
  imports: [
    NgbModule.forRoot(),
    BrowserModule,
    // ...
    HttpClientModule,
    HttpClientXsrfModule.withOptions(
      cookieName: 'XSRF-TOKEN',
      headerName: 'X-CSRF-TOKEN'
    ),
    // other imports
  ],
  providers: [],
  entryComponents: [WarningDialog],
  bootstrap: [AppComponent]
)
export class AppModule 

然后,在 Service 的方法中,我发出以下 http 请求(this.httpHttpClient 的一个实例):

this.http
    .post<any>('api/login', '_username': username, '_pass': password)
    .subscribe(/* handler here */);

发布请求从不发送 X-XSRF-TOKEN 标头。为什么?

【问题讨论】:

我猜你可以编写自己的拦截器来处理这个问题。我知道有些用户在not using absolute URLs 时遇到过这个问题 谢谢,我考虑过了,但我以更简洁的方式解决了问题:请看我的回答。 您好 Stefan,您能帮我在 Angular 版本 6 中以 PHP 作为后端生成 XSRF 令牌值吗?我无法 var_dump XSRF 令牌,因为我无法生成令牌在 typeScript Click Here 我已经发布了这个问题! @Nishanthॐ 请看看你的问题,我已经用代码示例添加了答案。 【参考方案1】:

问题又是 Angular 糟糕的文档。

事实是,Angular 将添加 X-XSRF-TOKEN 标头仅当XSRF-TOKEN cookie 是在服务器端生成并具有以下选项:

路径 = / httpOnly = false(这非常重要,并且完全未记录

此外,Angular 应用和被调用的 URL 必须驻留在同一台服务器上。

参考:this Angular Github issue

【讨论】:

除上述之外: - 没有为 GET 或 HEAD 请求设置标头 - cookie 的名称必须是:XSRF-TOKEN(除非默认名称被覆盖) - 最重要的是确保您不使用绝对路径。这意味着以 HTTP 或 HTTPS 开头的路径。它必须是相对路径。 这是否意味着我所有的服务器端请求都必须来自我的站点域而不是专用的 api url?例如: localhost:4000/home 是 SPA,我的 api 将位于 localhost:4000/api/getSomething 而不是 localhost:8080/api/getSomething @Anthony cookie 从服务器端页面设置,并从 SPA 中读取,因此,SPA 和 api 必须至少在同一第二级 域,以便可以从 SPA 中读取 cookie。如果服务器和 SPA 在同一域的不同 端口 上运行(如您的示例),它可以工作,但我没有测试过这种情况。 @Merv 说,在用户登录我的应用程序后,恶意用户以某种方式让受害者访问他们的恶意网站。这个恶意网站有如下内容:
。返回的数据是否可以发送给恶意用户?需要注意的是,我正在检查身份验证/授权。
@RuneStar 不,这是不可能的(只有 CSRF。)因为一切都发生在用户的机器上。因此,即使用户被欺骗,响应(带有敏感数据)也会到达用户的机器,因此恶意用户不会从中获得任何收益。恶意用户获得某些东西的唯一方法是,如果他代表用户做(操作/修改/编辑)对他/她有利的事情。就像可能使用您的网上银行向他的帐户汇款一样。并且用户将看到该消息(如果银行显示已发送资金的消息)....$ 已发送到“恶意用户帐户”。希望你能理解!【参考方案2】:

在我的团队中,问题在于我们使用的是绝对路径而不是相对路径。

所以不要使用绝对路径,例如:

this.http.post<any>("https://example.com/api/endpoint",data)

使用

this.http.post<any>("api/endpoint",data)

或使用

this.http.post<any>("//example.com/api/endpoint",data)

这是因为绝对路径被 HttpClientXsrfModule 上的 Angular 代码显式忽略 (see)

【讨论】:

感谢您对 Angular 代码的引用。现在说得通了。 是的,这行得通。不能是http://...,因为它没有设置标题并且不能在没有双斜杠的情况下设置,至少在ng serve期间不会,因为我收到CORS错误(我的environments.ts文件的基本API url为/ /example.test + 端口号,以便可以在 ng serve 上以及在我的本地机器上构建之后测试 http 请求)【参考方案3】:

有点跑题了,但是对于其他来这里的人,我在后端通过以下方式解决了这个问题(spring-boot

     /**
     * CORS config - used by cors() in configure() DO NOT CHANGE the METDHO NAME
     * 
     * @return
     */
    @Bean()
    public CorsConfigurationSource corsConfigurationSource() 
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Lists.newArrayList("http://localhost:4200"));
        configuration.setAllowedMethods(Lists.newArrayList("GET", "POST", "OPTIONS"));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Lists.newArrayList("x-xsrf-token", "XSRF-TOKEN"));
        configuration.setMaxAge(10l);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    

【讨论】:

【参考方案4】:

经过无数小时的努力,对我们有用的解决方案是将请求(在 Angular 中)从“https://example.com”更改为“//example.com”。

其他解决方案都不适合我们。

【讨论】:

到目前为止,这是唯一对我有用的解决方案。但我不明白为什么。 因为this【参考方案5】:

你应该把这个 withCredentials: true 的服务放在前端

例如:

this.http.put<class>(url, body,  withCredentials: true );

【讨论】:

【参考方案6】:

确保您的服务器在browser requests OPTIONS method 时允许X-CSRF-Token 标头。

例子:

Access-Control-Allow-Headers: X-CSRF-Token, Content-Type

参考:MDN Docs

【讨论】:

【参考方案7】:
    request = request.clone(
        withCredentials: true
      );

在 InterceptService 中,这适用于我

【讨论】:

【参考方案8】:

对于 Spring Boot 用户来说,下面这个花了我一段时间:

此外,Angular 应用程序和被调用的 URL 必须位于 同一台服务器。

我在本地主机上使用不同端口上的应用程序测试我的解决方案,它的工作方式就像它是同一个来源。

但问题发生在我更改上下文路径后:/api,这与原点不同,这就是为什么我认为 Angular 不会将 XSRF 令牌添加到请求中,所以我需要添加:

final CookieCsrfTokenRepository cookieCsrfTokenRepository =  CookieCsrfTokenRepository.withHttpOnlyFalse();
    cookieCsrfTokenRepository.setCookiePath("/");

将它们设置为相同的来源

Here is complex solution for diffrent context-path

【讨论】:

以上是关于Angular 6 不会将 X-XSRF-TOKEN 标头添加到 http 请求的主要内容,如果未能解决你的问题,请参考以下文章

Angular 6 Transform Animation 不会改变 Element 上的样式

Angular 6:在 *ngFor 中的 formArray 上使用 ngx-pagination 不会更改视图中的控件

Angular 6 - 无法订阅从服务返回的数据

Angular2 http post没有将json对象传递给MVC 6控制器动作

在 Angular 6 中连接到 Strophe.js

Angular 7:ng serve 工作,但控制台显示 Zone.js 错误并且应用程序不会加载