spring-security - Angular 5 无法从 HttpXsrfTokenExtractor 获取 XSRF token

标签 spring-security csrf webpack-dev-server angular5 x-xsrf-token

我正在尝试通过 发出 POST 请求绝对网址 到 Spring(基本身份验证)安全的 Rest API。
阅读 Angular 省略了插入 X-XSRF-TOKEN 自动进入请求 header 以获取绝对 URL,我尝试实现 HttpInterceptor 添加 token 。

在我原来的 /登录 POST 请求,我创建必要的 授权:基本 header 以确保 Spring 对请求进行身份验证。
返回的响应 header 包含预期的 设置cookie token :Set-Cookie:XSRF-TOKEN=4e4a087b-4184-43de-81b0-e37ef953d755; Path=/
但是,在我的自定义拦截器类中,当我尝试从注入(inject)的 获取 token 时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) 响应 header 返回 token /登录 要求。

我阅读了有关创建拦截器的相关帖子:angular4 httpclient csrf does not send x-xsrf-token

但除此之外,我无法找到 HttpXsrfTokenExtractor 的很多文档:
https://angular.io/api/common/http/HttpXsrfTokenExtractor

问:为什么是 HttpXsrfTokenExtractor.getToken() 返回空值?

此外,我将拦截器类作为提供程序添加到 应用程序模块 .
这是我的 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 上从 运行我的 Angular 前端。本地主机:3000 和我的 Spring Rest 后端来自 本地主机:8080 .
它们位于不同的端口,因此使用 发出 http 请求的原因绝对网址 .浏览器会阻止 设置Cookie 当它来自对不同域的请求的响应时工作?

我还能错过什么吗?

感谢您的任何帮助。

----------------------------------------------
[2018 年 1 月 7 日更新]
修复
我用 Webpack 开发服务器 提供 Angular 代码。我通过配置 解决了这个问题代理 对于指向我的后端 Rest API 的 url。
这意味着从浏览器发出的所有请求现在只发送到端口 的开发服务器。 3000 ,即使是 Rest API 调用。

webpack 开发服务器 看到任何具有配置模式(例如/api/...)的请求 url,它将替换为对 上的后端服务器的调用http://localhost:8080 (在我的情况下)。

这是我添加到 的内容开发服务器 我的部分 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 在任何一个。

设置开发服务器代理的额外好处是客户端请求不再是绝对的,因此我不需要更改所有用于生产的 Rest API 调用。

我希望这对遇到同样问题/理解的人有用。

Webpack 开发服务器代理文档引用:
https://webpack.js.org/configuration/dev-server/#devserver-proxy

最佳答案

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

关于spring-security - Angular 5 无法从 HttpXsrfTokenExtractor 获取 XSRF token ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48002670/

相关文章:

grails - Grails/Spring安全访问被拒绝

django - CSRF错误Django ajax .post没有表单

javascript - 为什么使用 POST 方法可以防止 json 劫持?

python - Django CSRF验证失败

node.js - npm 脱网失败

javascript - Webpack 显示错误的 eslint linting 错误

javascript - bundle.js 而不是 React 组件上的控制台日志记录错误

grails - 未使用Spring Security ui通过电子邮件注册创建用户

java - 无法使用 Spring Boot 和基于 Java 的配置注入(inject) UserDetailsManager

java - HazelCast- Activity session 列表