Angular 5 не может получить токен XSRF от HttpXsrfTokenExtractor

Я пытаюсь сделать POST-запрос через абсолютный URL-адрес к защищенному API-интерфейсу Spring (обычная проверка подлинности).
Прочитав, что Angular пропускает вставку X-XSRF-TOKEN в заголовок запроса автоматически для абсолютных URL-адресов, я попытался реализовать HttpInterceptor для добавления токена.

В моем исходном / входящем POST-запросе я создаю необходимую авторизацию: Базовый заголовок, чтобы Spring аутентифицировал запрос.
Возвращенный заголовок ответа содержит ожидаемый токен set-cookie:

Set-Cookie:XSRF-TOKEN=4e4a087b-4184-43de-81b0-e37ef953d755; Path=/

Однако в моем пользовательском классе перехватчика, когда я пытаюсь получить токен из введенного HttpXsrfTokenExtractor для следующего запроса, он возвращает ноль.

Вот код для моего класса перехватчика:

import {Injectable, Inject} from '@angular/core';
import { Observable } from 'rxjs/Rx';
import {HttpInterceptor, HttpXsrfTokenExtractor, HttpRequest, HttpHandler, 
HttpEvent} from '@angular/common/http';


@Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {

   constructor(private tokenExtractor: HttpXsrfTokenExtractor) {}

   intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

     let requestMethod: string = req.method;
     requestMethod = requestMethod.toLowerCase();

     if (requestMethod && (requestMethod === 'post' || requestMethod === 'delete' || requestMethod === 'put' )) {
         const headerName = 'X-XSRF-TOKEN';
         let token = this.tokenExtractor.getToken() as string;
         if (token !== null && !req.headers.has(headerName)) {
           req = req.clone({ headers: req.headers.set(headerName, token) });
         }
      }

    return next.handle(req);
   }
}


tokenExtractor.getToken () возвращает ноль в приведенном выше коде. Я ожидал, что он вернет токен из заголовка ответа Spring (Set-Cookie) моего предыдущего запроса / входа.

Я прочитал этот связанный пост для создания перехватчика:
Angular4 httpclient csrf не отправляет x-xsrf-токен

Но я не смог найти много документации для HttpXsrfTokenExtractor, кроме этого:
https://angular.io/api/common/http/HttpXsrfTokenExtractor

Вопрос: почему HttpXsrfTokenExtractor.getToken() возвращает ноль?

Кроме того, я добавил класс перехватчика в качестве провайдера в app.module.
Вот мой app.module.ts:

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { LocationStrategy, HashLocationStrategy, APP_BASE_HREF } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AlertModule } from 'ng2-bootstrap';
import { routing, appRouterProviders } from './app.routing';
import { HttpXsrfInterceptor } from './httpxrsf.interceptor';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './registration/register.component';
import { HomeComponent } from './home/home.component';

@NgModule({
    declarations: [AppComponent,
               LoginComponent,
               RegisterComponent,
               HomeComponent],
    imports: [BrowserModule,
          FormsModule,
          ReactiveFormsModule,
          HttpClientModule,
          HttpClientXsrfModule, // Adds xsrf support
          AlertModule.forRoot(),
          routing],
    schemas: [CUSTOM_ELEMENTS_SCHEMA],
    providers: [
        appRouterProviders,
        [{provide: APP_BASE_HREF, useValue: '/'}],
        [{provide: LocationStrategy, useClass: HashLocationStrategy}],
        [{provide: HTTP_INTERCEPTORS, useClass: HttpXsrfInterceptor, multi: true }]
    ],
    bootstrap: [AppComponent]
})
export class AppModule {}


Еще одна вещь, которую стоит отметить, это то, что я запускаю мой Angular-интерфейс на Node.js с localhost:3000 и мой Spring Rest с localhost: 8080. Они находятся на разных портах, и поэтому причина для выполнения http-запросов с абсолютными URL-адресами. Будет ли браузер предотвращать работу Set-Cookie, когда он приходит от ответа на запрос в другом домене?

Могу ли я пропустить что-нибудь еще?

Спасибо за любую помощь.

----------------------------------------------
[Обновлено 7 января 2018 года]

FIX

Я использую сервер Webpack для обслуживания кода Angular. Я обошел эту проблему, настроив прокси-сервер для URL-адресов, указывающих на мой внутренний API отдыха.

Это означает, что все запросы, сделанные от браузера, теперь только к dev-серверу на порту 3000, даже для вызовов API Rest.
Когда сервер разработки веб-пакетов видит URL-адреса запросов с настроенным шаблоном (например, /api/...), он заменяет обращения к внутреннему серверу по http://localhost:8080/ (в моем случае).

Это то, что я добавил в раздел devServer моего файла webpack.dev.js:

proxy: {
    '/api': {
    'target': 'http://localhost:8080',
    'pathRewrite': {'^/api' : ''}
    //Omits /api from the actual request. E.g. http://localhost:8080/api/adduser -> http://localhost:8080/adduser
    }
}



С этой настройкой я больше не делаю междоменные (кросс-исходные) запросы из кода Angular или больше не использую абсолютные URL. Это сильно упрощает ситуацию, так как я больше не сражаюсь с механизмом Angular XSRF (CSRF). Это просто работает по умолчанию. Мне также не нужно использовать HttpInterceptor, чтобы вручную вставить X-XSRF-TOKEN в любой из них.

Дополнительным преимуществом настройки прокси-сервера dev-сервера является то, что клиентские запросы больше не являются абсолютными, поэтому мне не нужно менять все вызовы Rest API для производства.

Я надеюсь, что это полезно для тех, кто страдает той же проблемой / пониманием.

Документация прокси сервера разработки Webpack ref:
https://webpack.js.org/configuration/dev-server/

2 ответа

Решение

Так как вы используете разные порты (3000 и 8080), вы делаете запрос кросс-источника, поэтому вы не сможете прочитать куки в клиенте, отправленном с сервера. Если вы хотите разделить ваш клиент и сервер таким образом, вам нужно использовать прокси, чтобы клиентские и серверные приложения обслуживались по одному протоколу (http/https), домену и порту. Если вы используете Spring Boot, я бы посоветовал вам взглянуть на Spring Cloud Netflix, в частности, Zuul ( https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html).

Используйте свойство withCredentials: true, чтобы оно (XSRF-TOKEN) было доступно для чтения кодом Angular, как показано ниже:

      const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
  withCredentials: true //this is required so that Angular returns the Cookies received from the server. The server sends cookies in Set-Cookie header. Without this, Angular will ignore the Set-Cookie header
}; 
Другие вопросы по тегам