web-dev-qa-db-ja.com

Django CSRF保護でangular2 httpリクエストを使用する正しい方法は何ですか?

Angular1では、$ http-providerを設定することで問題を解決できます。お気に入り:

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

Angular2で同じことをするための良い習慣は何ですか?

Angular2でhttpリクエストを処理するには、Httpクラスを使用する必要があります。もちろん、ポスト関数の各呼び出しにCSRF行を追加することはお勧めできません。

Angular2では、Angular2のHttpクラスを継承する独自のクラスを作成し、ポスト関数を再定義する必要があると思います。それは正しいアプローチですか、それともよりエレガントな方法ですか?

19
Victor K

Victor Kの回答は完全に有効ですが、angular 2.0.0-rc.2の時点で、以下のようにCookieXSRFStrategyを使用することをお勧めします。

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

Angular 2がリリースされたので、CookieXSRFStrategyを使用して、これを行う正しい方法は次のようです。

コアモジュール を使用するようにアプリケーションを構成しましたが、代わりにメインアプリケーションモジュールで同じことを実行できます。

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 {
}, 
19
David

Angular2のソリューションは、angular1ほど簡単ではありません。必要なもの:

  1. csrftoken cookie値を選択します。

  2. この値を追加して、X-CSRFTokenという名前のヘッダーをリクエストします。

私はこのスニペットを提供します:

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})
]);
13
Victor K

angularの新しいバージョンでは、デコレータで関数を呼び出すことはできません。ファクトリプロバイダを使用する必要があります。

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

次に、ファクトリを使用します。

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

それ以外の場合は、コンパイラが教えてくれます。また、ng build --watchは、もう一度開始するまでこのエラーを報告しないこともわかりました。

4
PeterS

私はこれに数日間苦労しました。この記事のアドバイスは良いですが、2017年8月の時点で非推奨になりました( https://github.com/angular/angular/pull/18906 )。 angular2の推奨されるアプローチは単純ですが、注意が必要です。

推奨されるアプローチは HttpClientXsrfModule を使用し、Djangoのデフォルトのcsrf保護を認識するように構成することです。 Django docs によると、DjangoはCookie csrftokenを送信し、クライアントがヘッダーを返すことを期待します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 が変更リクエストにのみ適用されるということです。

デフォルトでは、インターセプターはこのCookie [ヘッダー]をすべての変更リクエスト(POSTなど)で相対URLに送信しますが、GET/HEADリクエストや絶対URLを含むリクエストでは送信しません。

GET/HEADでミューテーションを実行するAPIをサポートする必要がある場合は、独自のカスタムインターセプターを作成する必要があります。あなたは問題の例と議論を見つけることができます ここ

2
user590028

ビクター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})]);

ブートストラップが効果があるかどうかわからないので、データをポストする場所でもこれを行いました。

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

現在、Httpサービスのラッパーサービスを使用して、カスタムヘッダーで何でも解決しています。ヘッダーを手動で追加し、値を格納/取得するための追加のサービスを注入できます。この戦略は、たとえば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});
  }
}
1
jberndsen