为什么添加 HTTP 拦截器会导致 CORS 错误?
Why does adding an HTTP Interceptor cause CORS errors?
我正在开发带有 PHP REST Api 的 Angular 6 应用程序。我的开发环境托管在我的本地主机上,所以首先我必须启用 Access-Control-Allow-Origin: * 来绕过我遇到的任何 CORS 错误。
最近我一直在尝试使用拦截器实现 JWT,以将它们添加到我的 HTTP 请求中。按照 google 中的示例启用解释器后,我又开始收到 CORS 错误:
blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header
我肯定它与我的拦截器有关,因为当我将其注释掉时,它又可以工作了。这是我想要开始工作的拦截器的代码:
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from
'@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/index';
import {AuthService} from './auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(public auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
req = req.clone({
setHeaders: {
'Content-Type' : 'application/json; charset=utf-8',
// 'Accept' : 'application/json',
'Authorization': `Bearer ${this.auth.getToken()}`
}
});
return next.handle(req);
}
}
我什至试图将其剥离以仅设置授权 header 可用。
如果我注释掉授权 header 并只留下 Content-Type,我仍然会收到 CORS 错误。如果我将 Content-type 更改为 plain/text,它可以工作,但我没有授权 header。如果我将内容类型保留为 plain/text 并添加授权 header,我会收到 CORS 错误。
此时我只想在 $_SERVER var 中查看授权 header 所以 PHP 非常容易测试:
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE,
OPTIONS');
header('Access-Control-Allow-Headers: Authorization, Access, Origin,
Content-Type, X-Auth-Token');
echo json_encode(array(
'status' => 0
,'message'=> $_SERVER
));
exit;
这是我的应用程序模块,显示拦截器已配置:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginFormComponent } from './login-form/login-form.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {AuthInterceptor} from './auth.interceptor';
import { MessagesComponent } from './messages/messages.component';
@NgModule({
declarations: [
AppComponent,
LoginFormComponent,
MessagesComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
这是我用来发送 HTTP 请求的登录服务:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ServerResponse } from './server-response';
import {MessagesService} from './messages.service';
import {AuthService} from './auth.service';
@Injectable({
providedIn: 'root'
})
export class LoginService {
constructor(private http: HttpClient,
private ms: MessagesService,
private auth: AuthService) { }
login(formData) {
this.ms.clear();
console.log('JSON data', JSON.stringify((formData)));
console.log(this.auth.getToken());
return this.http.post('http://jpapp.com/api/login/login.php', JSON.stringify(formData))
.subscribe((rs: ServerResponse) => {
console.log('%cPost Success', 'color: green;');
console.log(rs);
}, (error) => ({status: -99, message: error}));
}
}
如果我删除拦截器,Requet header 看起来像这样(这是我尝试将 content-type 更改为 plain/text 的想法):
Provisional headers are shown
Accept: application/json, text/plain, */*
Content-Type: text/plain
Origin: http://localhost:4200
Referer: http://localhost:4200/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
当我启用拦截器时,请求 header 看起来像这样:
Provisional headers are shown
Access-Control-Request-Headers: authorization,content-type
Access-Control-Request-Method: POST
Origin: http://localhost:4200
Referer: http://localhost:4200/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
我读到过我可以使用 chrome 扩展来绕过它,但我想先了解我的代码中是否有一些东西,然后再安装扩展来尝试。
欢迎提供任何信息!
更新
edit:我应该提到将 req.clone() 设置为空白的原因我发现是因为预检只发生在 complex 而不是 simple cors 请求。一旦您添加自定义 header,在我的情况下,授权或执行任何导致它不再是简单请求的操作,默认情况下,它会使用 OPTIONS 方法发送预检请求,而不是在我的情况下, POST。这篇文章很好地解释了 cors 和一个简单与复杂的请求:https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
预检响应错误与代码本身无关。问题出在我的 IIS 10 Web 服务器的配置上。
在这篇文章之后:CORS Module Configuration Reference 我将所需的 cors 节点添加到我的 web.config 文件中,现在它可以正常工作了
我能够通过安装 Allow-Control-Allow-Origin: * Chrome 扩展绕过预检请求 CORS 错误
https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en
它似乎仍然只有在我保持默认 URL 匹配 *://*/*
时才有效 如果我更改它以匹配我的 URLs 我再次收到 CORS 错误,但这可能只是是我的模式匹配的问题。如果能解释一下它是如何工作的就好了。
我仍在寻找为什么通过拦截器传递授权会破坏服务器的原因Access-Control-Allow-Origin:* 因为我确信一旦我实际部署此应用程序我会再次遇到这个问题和 api。在默认配置下使用此扩展也是一个 PITA,因为它会破坏一堆网站。
我在登录中获取令牌时遇到了同样的问题,我通过在拦截器请求中添加一个if-else解决了
const yourToken = this.auth.getToken();
if (yourToken) {
req = req.clone({
setHeaders: {
'Content-Type': 'application/json; charset=utf-8',
Accept: 'application/json',
Authorization: `Bearer ${yourToken}`
}
});
} else {
req = req.clone({
setHeaders: {
'Content-Type': 'application/json; charset=utf-8',
Accept: 'application/json'
}
});
}
return next.handle(req);
我通过检查我的自定义 header 是否存在于请求中解决了这个问题,每当我将 headers 添加到 get 请求时我注意到错误并且在没有 [=13= 的情况下工作正常]s。所以我改为这样做
if (!(req.headers.get('No-auth')) || ((req.headers.get('No-auth') === 'True') && (req.headers.get('TokenRefresh') === 'False')))enter code here { return next.handle(req.clone());}
如果请求中没有自定义 header,则表示未提供 header,这样做只是克隆原始请求。
我正在开发带有 PHP REST Api 的 Angular 6 应用程序。我的开发环境托管在我的本地主机上,所以首先我必须启用 Access-Control-Allow-Origin: * 来绕过我遇到的任何 CORS 错误。
最近我一直在尝试使用拦截器实现 JWT,以将它们添加到我的 HTTP 请求中。按照 google 中的示例启用解释器后,我又开始收到 CORS 错误:
blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header
我肯定它与我的拦截器有关,因为当我将其注释掉时,它又可以工作了。这是我想要开始工作的拦截器的代码:
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from
'@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/index';
import {AuthService} from './auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(public auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
req = req.clone({
setHeaders: {
'Content-Type' : 'application/json; charset=utf-8',
// 'Accept' : 'application/json',
'Authorization': `Bearer ${this.auth.getToken()}`
}
});
return next.handle(req);
}
}
我什至试图将其剥离以仅设置授权 header 可用。
如果我注释掉授权 header 并只留下 Content-Type,我仍然会收到 CORS 错误。如果我将 Content-type 更改为 plain/text,它可以工作,但我没有授权 header。如果我将内容类型保留为 plain/text 并添加授权 header,我会收到 CORS 错误。
此时我只想在 $_SERVER var 中查看授权 header 所以 PHP 非常容易测试:
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE,
OPTIONS');
header('Access-Control-Allow-Headers: Authorization, Access, Origin,
Content-Type, X-Auth-Token');
echo json_encode(array(
'status' => 0
,'message'=> $_SERVER
));
exit;
这是我的应用程序模块,显示拦截器已配置:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginFormComponent } from './login-form/login-form.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {AuthInterceptor} from './auth.interceptor';
import { MessagesComponent } from './messages/messages.component';
@NgModule({
declarations: [
AppComponent,
LoginFormComponent,
MessagesComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FormsModule,
ReactiveFormsModule
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
这是我用来发送 HTTP 请求的登录服务:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ServerResponse } from './server-response';
import {MessagesService} from './messages.service';
import {AuthService} from './auth.service';
@Injectable({
providedIn: 'root'
})
export class LoginService {
constructor(private http: HttpClient,
private ms: MessagesService,
private auth: AuthService) { }
login(formData) {
this.ms.clear();
console.log('JSON data', JSON.stringify((formData)));
console.log(this.auth.getToken());
return this.http.post('http://jpapp.com/api/login/login.php', JSON.stringify(formData))
.subscribe((rs: ServerResponse) => {
console.log('%cPost Success', 'color: green;');
console.log(rs);
}, (error) => ({status: -99, message: error}));
}
}
如果我删除拦截器,Requet header 看起来像这样(这是我尝试将 content-type 更改为 plain/text 的想法):
Provisional headers are shown
Accept: application/json, text/plain, */*
Content-Type: text/plain
Origin: http://localhost:4200
Referer: http://localhost:4200/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
当我启用拦截器时,请求 header 看起来像这样:
Provisional headers are shown
Access-Control-Request-Headers: authorization,content-type
Access-Control-Request-Method: POST
Origin: http://localhost:4200
Referer: http://localhost:4200/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
我读到过我可以使用 chrome 扩展来绕过它,但我想先了解我的代码中是否有一些东西,然后再安装扩展来尝试。
欢迎提供任何信息!
更新
edit:我应该提到将 req.clone() 设置为空白的原因我发现是因为预检只发生在 complex 而不是 simple cors 请求。一旦您添加自定义 header,在我的情况下,授权或执行任何导致它不再是简单请求的操作,默认情况下,它会使用 OPTIONS 方法发送预检请求,而不是在我的情况下, POST。这篇文章很好地解释了 cors 和一个简单与复杂的请求:https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
预检响应错误与代码本身无关。问题出在我的 IIS 10 Web 服务器的配置上。
在这篇文章之后:CORS Module Configuration Reference 我将所需的 cors 节点添加到我的 web.config 文件中,现在它可以正常工作了
我能够通过安装 Allow-Control-Allow-Origin: * Chrome 扩展绕过预检请求 CORS 错误
https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en
它似乎仍然只有在我保持默认 URL 匹配 *://*/*
时才有效 如果我更改它以匹配我的 URLs 我再次收到 CORS 错误,但这可能只是是我的模式匹配的问题。如果能解释一下它是如何工作的就好了。
我仍在寻找为什么通过拦截器传递授权会破坏服务器的原因Access-Control-Allow-Origin:* 因为我确信一旦我实际部署此应用程序我会再次遇到这个问题和 api。在默认配置下使用此扩展也是一个 PITA,因为它会破坏一堆网站。
我在登录中获取令牌时遇到了同样的问题,我通过在拦截器请求中添加一个if-else解决了
const yourToken = this.auth.getToken();
if (yourToken) {
req = req.clone({
setHeaders: {
'Content-Type': 'application/json; charset=utf-8',
Accept: 'application/json',
Authorization: `Bearer ${yourToken}`
}
});
} else {
req = req.clone({
setHeaders: {
'Content-Type': 'application/json; charset=utf-8',
Accept: 'application/json'
}
});
}
return next.handle(req);
我通过检查我的自定义 header 是否存在于请求中解决了这个问题,每当我将 headers 添加到 get 请求时我注意到错误并且在没有 [=13= 的情况下工作正常]s。所以我改为这样做
if (!(req.headers.get('No-auth')) || ((req.headers.get('No-auth') === 'True') && (req.headers.get('TokenRefresh') === 'False')))enter code here { return next.handle(req.clone());}
如果请求中没有自定义 header,则表示未提供 header,这样做只是克隆原始请求。