为什么添加HTTP拦截器会导致CORS错误?

时间:2019-02-05 17:54:56

标签: angular cors interceptor

我正在使用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);
  }
}

我什至试图将其剥离以仅将Authorization标头设置为可用。

如果我注释掉Authorization标头并只留下Content-Type,我仍然会收到CORS错误。如果我将Content-type更改为plain / text,它可以工作,但是我没有授权标头。如果将内容类型保留为纯文本/纯文本并添加Authorization标头,则会收到CORS错误。

这时,我只想在$ _SERVER变量中看到授权标头,因此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头看起来像这样(这是我尝试将内容类型更改为纯文本/文本的地方)

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

当我启用拦截器后,Request标头如下所示:

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扩展程序绕过它,但想在安装扩展程序之前先了解我的代码中是否有内容。

任何信息都值得赞赏!

3 个答案:

答案 0 :(得分:0)

我在登录中获取令牌时遇到了同样的问题,我通过在拦截器请求中添加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);

答案 1 :(得分:0)

我还通过检查请求中是否存在我的自定义标头来解决此问题,每当我向 get 请求添加标头时我都会注意到错误,并且在没有标头的情况下也能正常工作。所以我做了这个 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());} 如果请求中没有自定义标头,则表示未提供标头,这样做只会克隆原始请求。

答案 2 :(得分:-1)

更新

编辑:我应该提到将req.clone()设置为空白的原因,因为我发现预检仅发生在 complex 上,而不是简单要求。一旦添加了自定义标头(在我的情况下为Authorization或执行任何使它不再是简单请求的操作),默认情况下,便会使用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绕过预检请求CORS错误:* Chrome扩展程序 https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en

似乎只有在保持默认URL匹配*://*/*的情况下仍然有效,如果我将其更改为与URL匹配的话,我会再次遇到CORS错误,但这可能只是我的模式匹配问题。如果对它的工作原理有一个解释,那就太好了。

我仍在寻找为什么通过拦截器传递授权会破坏服务器的原因Access-Control-Allow-Origin:*因为我确定我会遇到这个问题再次,实际上我部署了此应用程序和API。将此扩展名与默认配置一起使用也是PITA,因为它将破坏大量站点。