web-dev-qa-db-fra.com

Quelle est la bonne façon d'utiliser les requêtes http angular2 avec Django CSRF protection?

Dans Angular1, le problème peut être résolu en configurant $ http-provider. Comme:

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

Quelle est la bonne pratique pour faire de même dans Angular2?

Dans Angular2 pour travailler avec les requêtes http, nous devons utiliser la classe Http. Bien sûr, ce n'est pas une bonne pratique d'ajouter une ligne CSRF à chaque appel de post-fonction.

Je suppose que dans Angular2, je devrais créer ma propre classe qui hérite de la classe Http d'Angular2 et redéfinir la post-fonction. Est-ce la bonne approche ou existe-t-il une méthode plus élégante?

19
Victor K

La réponse de Victor K est parfaitement valable cependant à partir de angular 2.0.0-rc.2, une approche préférée serait d'utiliser CookieXSRFStrategy comme ci-dessous,

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

Maintenant que Angular 2 est publié, ce qui suit semble être la bonne façon de procéder, en utilisant CookieXSRFStrategy.

J'ai configuré mon application pour avoir un module principal mais vous pouvez faire la même chose dans votre module d'application principal à la place:

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

La solution pour Angular2 n'est pas aussi simple que pour angular1. Vous avez besoin:

  1. Sélectionnez csrftoken valeur du cookie.

  2. Ajoutez cette valeur pour demander des en-têtes avec le nom X-CSRFToken.

J'offre cet extrait:

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

Pour les versions ultérieures de angular vous ne pouvez pas appeler de fonctions dans les décorateurs. Vous devez utiliser un fournisseur d'usine:

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

Et puis utilisez l'usine:

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

Sinon, le compilateur vous le dira. Ce que j'ai également vu, c'est que ng build --watch ne signalera pas cette erreur tant que vous ne l'aurez pas recommencé.

4
PeterS

J'ai lutté avec ça pendant quelques jours. Les conseils de cet article sont bons, mais en août 2017 est obsolète ( https://github.com/angular/angular/pull/18906 ). L'approche recommandée angular2 est simple, mais a une mise en garde.

L'approche recommandée consiste à utiliser HttpClientXsrfModule et à le configurer pour reconnaître la protection csrf par défaut de Django. Selon le Django docs , Django enverra le cookie csrftoken et attendra du client qu'il retourne l'en-tête X-CSRFToken. Dans angular2, ajoutez ce qui suit à votre app.module.ts

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

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

La mise en garde est que angular2's XSRF Protection s'applique uniquement aux demandes de mutation:

Par défaut, un intercepteur envoie ce cookie [en-tête] sur toutes les demandes de mutation (POST, etc.) aux URL relatives mais pas sur les demandes GET/HEAD ou sur les demandes avec une URL absolue.

Si vous devez prendre en charge une API qui effectue une mutation sur GET/HEAD, vous devrez créer votre propre intercepteur personnalisé. Vous pouvez trouver un exemple et une discussion de la question ici .

2
user590028

Victor K avait la solution, je vais juste ajouter ce commentaire ici sur ce que j'ai fait:

J'ai créé le composant "ExRequestOptions" comme l'a dit Victor K, mais j'ai également ajouté une méthode "appendHeaders" à ce composant:

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

Ensuite, j'ai eu cela dans mon 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})]);

Je ne suis pas sûr que le bootstrapping ait eu un effet, alors j'ai également fait cela où je publierais des données:

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

Actuellement, je résout tout avec des en-têtes personnalisés en utilisant un service wrapper autour du service Http. Vous pouvez ajouter n'importe quel en-tête manuellement et injecter des services supplémentaires pour stocker/récupérer des valeurs. Cette stratégie fonctionne également pour les JWT, par exemple. Jetez un œil au code ci-dessous, j'espère que cela vous aidera.

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