Spring Security Angular 5无法从HttpXsrfTokenExtractor获取XSRF令牌

wlzqhblo  于 2023-10-20  发布在  Spring
关注(0)|答案(2)|浏览(124)

我正在尝试通过绝对URL向Spring(基本身份验证)安全的Rest API发出POST请求。
在阅读了Angular省略了将X-XSRF-TOKEN自动插入绝对URL的请求头部后,我尝试实现一个HttpInterceptor来添加令牌。
在我最初的**/signinPOST请求中,我创建了必要的授权:Basicheader确保Spring对请求进行身份验证。
返回的响应头包含预期的
set-cookietoken:
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 does not send 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 {}

另一件需要注意的事情是,我在Node.js上从localhost:3000运行Angular前端,在Spring Rest上从localhost:8080运行后端。它们在不同的端口上,因此使用绝对网址进行http请求的原因。当Set-Cookie来自不同域的请求响应时,浏览器是否会阻止它工作?
我还能错过什么吗
谢谢你的帮助。

[2018年1月7日更新]
修复

我使用Webpack dev server来提供Angular代码。我通过为指向后端Rest API的URL配置代理来解决这个问题。
这意味着从浏览器发出的所有请求现在都只能发送到dev服务器的端口3000,即使是Rest API调用。
webpack dev server看到任何带有配置模式的请求url(例如/API/.)时,它会替换为对**http://localhost:8080上后端服务器的调用(在我的情况下)。
这是我在
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
设置开发服务器代理的额外好处是客户端请求不再是绝对的,因此我不需要为生产更改所有Rest API调用。
我希望这对任何有同样问题/理解的人都有用。
Webpack开发服务器代理文档参考:
https://webpack.js.org/configuration/dev-server/#devserver-proxy

a2mppw5e

a2mppw5e1#

由于您使用不同的端口(3000和8080),您正在进行跨域请求,因此您将无法读取客户端中从服务器发送的cookie。如果你想以这种方式分离你的客户端和服务器,你需要使用一个代理,这样客户端和服务器应用程序就可以从相同的协议(http/https)、域和端口得到服务。如果你使用的是Sping Boot ,我建议你看看Spring Cloud Netflix,特别是Zuul(https://cloud.spring.io/spring-cloud-networks/single/spring-cloud-networks.html #_router_and_filter_zuul)。

w80xi6nr

w80xi6nr2#

与凭据一起使用: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
};

相关问题