使用带有 Django CSRF 保护的 angular2 http 请求的正确方法是什么?

What is the right way to use angular2 http requests with Django CSRF protection?

在Angular1中可以通过配置$http-provider来解决这个问题。喜欢:

app.config(function($httpProvider) {
  $httpProvider.defaults.xsrfCookieName = 'csrftoken';
  $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
});

在 Angular2 中执行相同操作的最佳做​​法是什么?

在 Angular2 中处理 http 请求我们需要使用 class Http。当然,在 post-function.

的每次调用中添加 CSRF 行并不是一个好的做法。

我想在 Angular2 中我应该创建自己的 class 继承 Angular2 的 Http class 并重新定义 post 函数。是正确的做法还是有更优雅的方法?

Angular2 的解决方案不像 angular1 那样容易。 您需要:

  1. 选择 csrftoken cookie 值。

  2. 将此值添加到名称为 X-CSRFToken 的请求 headers。

我提供这个片段:

import {Injectable, provide} from 'angular2/core';
import {BaseRequestOptions, RequestOptions} from 'angular2/http'

@Injectable()
export class ExRequestOptions extends BaseRequestOptions  {
  constructor() {
    super();
    this.headers.append('X-CSRFToken', this.getCookie('csrftoken'));
  }

  getCookie(name) {
    let value = "; " + document.cookie;
    let parts = value.split("; " + name + "=");
    if (parts.length == 2) 
      return parts.pop().split(";").shift();
  }
}

export var app = bootstrap(EnviromentComponent, [
  HTTP_PROVIDERS,
  provide(RequestOptions, {useClass: ExRequestOptions})
]);

Victor K 找到了解决方案,我将在此处添加关于我所做的评论:

我按照 Victor K 所说创建了组件 "ExRequestOptions",但我还向该组件添加了一个方法 "appendHeaders":

appendHeaders(headername: string, headervalue: string) {
    this.headers.append(headername, headervalue);
}

然后我的 main.ts:

import {bootstrap}    from 'angular2/platform/browser'
import {AppComponent} from './app.component'
import {HTTP_PROVIDERS, RequestOptions} from 'angular2/http';
import 'rxjs/Rx';
import {ExRequestOptions} from './transportBoxes/exRequestOptions';
import {provide} from 'angular2/core';

bootstrap(AppComponent,[ HTTP_PROVIDERS,  
    provide(RequestOptions, {useClass: ExRequestOptions})]);

我不确定引导程序是否有任何效果,所以我也是这样做的 我会 post 数据:

    let options = new ExRequestOptions();
    options.appendHeaders('Content-Type', 'application/json');
    return this.http.post('.....URL', JSON.stringify(registration),
         options)

Victor K 的回答完全有效,但是从 angular 2.0.0-rc.2 开始,首选方法是使用 CookieXSRFStrategy,如下所示,

bootstrap(AngularApp, [
  HTTP_PROVIDERS,
  provide(XSRFStrategy, {useValue: new CookieXSRFStrategy('csrftoken', 'X-CSRFToken')})
]);

现在 Angular 2 已发布,以下似乎是正确的方法,使用 CookieXSRFStrategy

我已将我的应用程序配置为具有 core module,但您可以在主应用程序模块中执行相同的操作:

import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule }   from '@angular/common';
import { HttpModule, XSRFStrategy, CookieXSRFStrategy } from '@angular/http';

@NgModule({
    imports: [
        CommonModule,
        HttpModule
     ],
    declarations: [ ],
    exports: [ ],
    providers: [
        {
            provide: XSRFStrategy,
            useValue: new CookieXSRFStrategy('csrftoken', 'X-CSRFToken')
        }
    ]
})


export class CoreModule {
}, 

目前,我使用围绕 Http 服务的包装器服务解决任何自定义 headers 问题。您可以手动添加任何 header 并为 storing/retrieving 值注入额外的服务。例如,此策略也适用于 JWT。看看下面的代码,希望对您有所帮助。

import {Injectable} from '@angular/core';
import {Http, Headers, RequestOptions} from '@angular/http';

@Injectable()
export class HttpService {
  constructor(private http: Http) {
  }

  private get xsrfToken() {
    // todo: some logic to retrieve the cookie here. we're in a service, so you can inject anything you'd like for this
    return '';
  }

  get(url) {
    return this.http.get(url, this.getRequestOptions())
      .map(result => result.json())
      .catch(error => error.json());
  }

  post(url, payload) {
    return this.http.post(url, payload, this.getRequestOptions())
      .map(result => result.json())
      .catch(error => error.json());
  }

  private getRequestOptions() {
    const headers = new Headers({'Content-Type': 'application/json', 'X-XSRF-TOKEN': this.xsrfToken});
    return new RequestOptions({headers: headers});
  }
}

对于 angular 的更高版本,您不能在装饰器中调用函数。您必须使用工厂供应商:

export function xsrfFactory() {
  return new CookieXSRFStrategy('_csrf', 'XSRF-TOKEN');
}

然后使用工厂:

  providers: [
    {
      provide: XSRFStrategy,
      useFactory : xsrfFactory
  }],

否则编译器会告诉你。 我还看到 ng build --watch 在您再次启动它之前不会报告此错误。

我为此苦苦挣扎了几天。本文中的建议很好,但截至 2017 年 8 月已弃用 (https://github.com/angular/angular/pull/18906)。 angular2 推荐的方法很简单,但有一个警告。

推荐的方法是使用 HttpClientXsrfModule and to configure it to recognize django's default csrf protection. According to the django docs,django 将发送 cookie csrftoken 并期望客户端 return header X-CSRFToken。在 angular2 中,将以下内容添加到您的 app.module.ts

import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';

@NgModule({
  imports: [
    HttpClientModule,
    HttpClientXsrfModule.withOptions({
      cookieName: 'csrftoken',
      headerName: 'X-CSRFToken',
    })
  ], ...

需要注意的是 angular2 的 XSRF Protection 仅适用于变异请求:

By default, an interceptor sends this cookie [header] on all mutating requests (POST, etc.) to relative URLs but not on GET/HEAD requests or on requests with an absolute URL.

如果您需要支持对 GET/HEAD 执行突变的 API,您将需要创建自己的自定义拦截器。您可以找到问题的示例和讨论 here