web-dev-qa-db-ja.com

Angular 5HttpXsrfTokenExtractorからXSRFトークンを取得できません

絶対URLを介してSpring(基本認証)で保護されたRestAPIにPOSTリクエストを送信しようとしています。
Angularは絶対URLのリクエストヘッダーへのX-XSRF-TOKENの挿入を自動的に省略します)を読んだので、HttpInterceptortoにトークンを追加します。

元の/ signinPOSTリクエストでは、Springがリクエストを認証するように、必要なauthorization:Basicヘッダーを作成します。
返される応答ヘッダーには、予期されるset-cookieトークン:

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

ただし、カスタムインターセプタークラスで、注入されたHttpXsrfTokenExtractorから次のリクエストのためにトークンを取得しようとすると、null)が返されます。

これが私のインターセプタークラスのコードです:

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()は上記のコードでnullを返します。以前の/ signinリクエストのSpring(Set-Cookie)応答ヘッダーからトークンを返すことを期待していました。

インターセプターを作成するために、この関連記事を読みました。
angular4 httpclient csrfはx-xsrf-tokenを送信しません

しかし、これ以外にHttpXsrfTokenExtractorのドキュメントを見つけることができませんでした。
https://angular.io/api/common/http/HttpXsrfTokenExtractor

質問:HttpXsrfTokenExtractor.getToken()がnullを返すのはなぜですか?

さらに、インターセプタークラスをプロバイダーとして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 {}


もう1つ注意すべき点は、AngularフロントエンドをNode.jsでlocalhost:3000から)、Spring Restバックエンドをlocalhostから)実行していることです。 :8080。それらは異なるポート上にあるため、絶対URLでhttpリクエストを行う理由。ブラウザは、リクエストの応答から来るときにSet-Cookieが機能しないようにしますか?別のドメイン?

他に何か足りないものはありますか?

助けてくれてありがとう。

----------------------------------------------
[2018年1月7日更新]

[〜#〜]修正[〜#〜]

私はWebpack dev serverを使用してAngularコードを提供します。私の背中を指すURLに対してプロキシを構成することで、この問題を回避しました- RestAPIを終了します。

これは、ブラウザーから行われたすべてのリクエストが、Rest API呼び出しの場合でも、ポート00)の開発サーバーに対してのみ行われるようになったことを意味します。
webpack dev serverが設定されたパターン(例:/ api/...)のリクエストURLを検出すると、http)上のバックエンドサーバーへの呼び出しに置き換えられます:// localhost:808(私の場合)。

これは、webpack.dev.jsファイルのdevServerセクションに追加したものです:

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どちらかで。

開発サーバープロキシを設定することの追加の利点は、クライアントリクエストが絶対的ではなくなったことです。そのため、本番用にすべてのRESTAPI呼び出しを変更する必要はありません。

これが同じ問題/理解に苦しんでいる人に役立つことを願っています。

Webpack開発サーバープロキシドキュメント参照:
https://webpack.js.org/configuration/dev-server/#devserver-proxy

8
Glen Au-Yeung

異なるポート(3000と8080)を使用しているため、クロスオリジンリクエストを行っているため、サーバーから送信されたクライアントでCookieを読み取ることができません。この方法でクライアントとサーバーを分離する場合は、プロキシを使用して、クライアントとサーバーのアプリケーションが同じプロトコル(http/https)、ドメイン、およびポートから提供されるようにする必要があります。 Spring Bootを使用している場合は、Spring Cloud Netflix、特にZuul( https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#_router_and_filter_zuul)を確認することをお勧めします。 )。

2
dspies