Как правильно использовать http-запросы angular2 с защитой Django CSRF?
в Angular1 проблема может быть решена путем настройки $http-provider. Например:
app.config(function($httpProvider) {
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
});
что такое хорошая практика, чтобы сделать то же самое в Angular2?
в Angular2 для работы с http-запросами нам нужно использовать класс Http. Конечно, это не очень хорошая практика для добавления CSRF-line к каждому вызову post-функции.
Я думаю, что в Angular2 я должен создать собственный класс, который наследует Http-класс Angular2 и переопределить пост-функцию. Это правильный подход или есть более элегантный метод?
7 ответов
ответ Виктора к совершенно действителен, однако, с углового 2.0.0-rc.2, предпочтительным подходом было бы использовать CookieXSRFStrategy, как показано ниже,
bootstrap(AngularApp, [
HTTP_PROVIDERS,
provide(XSRFStrategy, {useValue: new CookieXSRFStrategy('csrftoken', 'X-CSRFToken')})
]);
теперь, когда 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 {
},
решение для Angular2 не так просто, как для angular1. Вам нужно:
выбрать
csrftoken
значение cookie.добавьте это значение для запроса заголовков с именем
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})
]);
для более поздних версий angular вы не можете вызывать функции в декораторах. Вы должны использовать поставщика фабрики:
export function xsrfFactory() {
return new CookieXSRFStrategy('_csrf', 'XSRF-TOKEN');
}
и затем использовать фабрику:
providers: [
{
provide: XSRFStrategy,
useFactory : xsrfFactory
}],
в противном случае компилятор будет выдавать. Я также видел, что ng build -- watch не сообщит об этой ошибке, пока вы не запустите ее снова.
У Виктора К было решение, я просто добавлю этот комментарий здесь о том, что я сделал:
Я создал компонент "ExRequestOptions", как сказал Виктор К, но я также добавил метод" appendHeaders " к этому компоненту:
appendHeaders(headername: string, headervalue: string) {
this.headers.append(headername, headervalue);
}
тогда у меня было это в моей основной.ТС:
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)
я боролся с этим в течение нескольких дней. Советы в этой статье хороши, но по состоянию на август 2017 года устарели (https://github.com/angular/angular/pull/18906). Рекомендуемый подход angular2 прост, но имеет оговорку.
рекомендуемый подход заключается в использовании HttpClientXsrfModule и настроить его для распознавания защиты 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 применяется только к мутации запросы:
по умолчанию перехватчик отправляет этот файл cookie [заголовок] на все изменяющиеся запросы (POST, etc.) к относительным URL, но не на GET/HEAD запросов или на запросы с абсолютным URL.
Если вам нужно поддерживать API, который выполняет мутацию на GET/HEAD, вам нужно будет создать свой собственный перехватчик. Вы можете найти пример и обсуждение вопроса здесь.
В настоящее время я решаю что-либо с пользовательскими заголовками, используя службу-оболочку вокруг службы Http. Вы можете добавить любой заголовок вручную и ввести дополнительные службы для хранения/получения значений. Эта стратегия также работает для JWTs, например. Посмотрите на код ниже, я надеюсь, что это поможет.
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});
}
}