响应预检请求未通过访问控制检查 - 没有'Access-Control-Allow-Origin'头部

710
我在使用ngResource调用REST API时遇到了这个错误,API是在Amazon Web Services上的:

XMLHttpRequest无法加载http://server.apiurl.com:8000/s/login?login=facebook。预检请求的响应未通过访问控制检查:所请求的资源上没有'Access-Control-Allow-Origin'头部。因此,来自'http://localhost'的访问被拒绝。错误 405

服务:

socialMarkt.factory('loginService', ['$resource', function ($resource) {
    var apiAddress = "http://server.apiurl.com:8000/s/login/";
    return $resource(apiAddress, {
        login: "facebook",
        access_token: "@access_token",
        facebook_id: "@facebook_id"
    }, {
        getUser: {
            method: 'POST'
        }
    });
}]);

控制器:

[...]
loginService.getUser(JSON.stringify(fbObj)),
    function (data) {
        console.log(data);
    },
    function (result) {
        console.error('Error', result.status);
    }
[...]

我正在使用Chrome。为了解决这个问题,我还能做些什么?

我甚至已经配置了服务器以接受来自localhost的头信息。


1
你是在“配置服务器”还是在“亚马逊网络服务上创建REST API”? - dandavis
3
你显然没有在服务器端足够地启用CORS。请提供响应头的示例。 - charlietfl
4
不管怎样,你的负评都是错误的。他正在将文件托管在本地计算机上。无论他在后端进行什么样的配置,都没有意义。Angular 不会允许这种预检请求。 - user796446
3
谢谢您的评论,在我将浏览器安全设置关闭后,问题得以解决。 - Andre Mendes
3
但是关闭安全性只是一种牺牲安全性的丑陋权宜之计,无法解决你的问题。 - shivi
显示剩余3条评论
28个回答

331

你遇到了CORS问题。

有几种方法可以修复或解决这个问题。

  1. 关闭CORS。例如:如何在Chrome中关闭CORS
  2. 使用浏览器插件
  3. 使用代理,例如nginx设置示例
  4. 完成必要的服务器设置。这更多地取决于您在EC2实例上加载的Web服务器(假设这是您所说的“Amazon Web服务”)。对于您特定的服务器,您可以参考启用CORS网站
更详细地说,您正在尝试从localhost访问api.serverurl.com。这正是跨域请求的确切定义。
要么关闭它以完成您的工作(可以,但如果您访问其他站点,则安全性较差,并且只会推迟问题),要么使用代理,使您的浏览器认为所有请求都来自本地主机,而实际上您有一个本地服务器,然后调用远程服务器。
因此,api.serverurl.com 可能变成 localhost:8000/api,您的本地 nginx 或其他代理将发送到正确的目标。

现在根据广大用户的需求,100%更多的CORS信息——同样美味!


绕过CORS正是为那些刚开始学习前端的人所展示的。 使用Promises的HTTP示例


131
在浏览器中禁用CORS?这不是一种解决方案,而是一个绕过方式,不能帮助那些真正需要启用CORS的人。 - Jonathan Simas
4
正如所述,这是继续开发工作的几种方法之一。解决问题的方法就是解决方案。你会注意到在我的回答中我说这是不安全的解决方法,只是把问题推到了以后才去解决它。;-) - user796446
2
你至少应该提到在许多情况下适用的解决方案是设置CORS。如果这样做,就不会再有太多负面评价了。 - YakovL
6
在Chrome中关闭CORS或使用Chrome插件从来没有一个真正的解决方案(如果网站存在CORS问题,你会要求每个客户在之前都禁用他们的CORS吗?)。感谢回复,但我更希望你将这两个点标记为虚假解决方案。真正的解决方案是配置WS/API以允许被允许的来源。 - Mahefa
@Mahefa,我向您引荐答案中的最后一个链接。有时候为了开发目的关闭CORS是完全有效的。https://codecraft.tv/courses/angular/http/http-with-promises/ - user796446
显示剩余5条评论

229

我的“API服务器”是一个PHP应用程序,因此为了解决这个问题,我找到了以下解决方案:

将下面这些行放在文件index.php中:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');

13
同意,这个答案比被接受的答案更好,但是在复制这些代码时要小心,确保修改方法和源代码。 - Amir Savand
@Slipstream 这个操作是在接收服务器还是发送post请求的文件所在的服务器上完成的? - user14983900
@AlphaMirage 在接收服务器上 - Slipstream
@Slipstream 我已经尝试过了... 但还是不行!我正在使用node.js和express.js。我应该展示我的代码吗?我们可以在聊天室里讨论吗?谢谢! - user14983900
在添加了这些头部信息之后,仍然出现相同的错误。 - strix25
显示剩余5条评论

62
ASP.NET Core Web API 中,通过添加 "Microsoft.AspNetCore.Cors"(版本1.1.1)并在 Startup.cs 中进行以下更改来解决此问题。
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy("AllowAllHeaders",
            builder =>
            {
                builder.AllowAnyOrigin()
                       .AllowAnyHeader()
                       .AllowAnyMethod();
            });
    });
    .
    .
    .
}

并且

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    // Shows UseCors with named policy.
    app.UseCors("AllowAllHeaders");
    .
    .
    .
}

在控制器中加入[EnableCors("AllowAllHeaders")]


32
如果你想要在网站中引入跨站脚本漏洞,这样的回答就很好了!但是请不要这样做!指定你能够访问的域名以避免安全问题,CORS 的存在就是为了这个原因。 - crthompson
3
为了让@paqogomez更清楚,在您的ConfigureServices方法中: services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", builder => { builder.WithOrigins("http://localhost") .AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod(); }); }); 在您的Configure方法中: app.UseCors("AllowSpecificOrigin"); - Tena

49

在处理CORS时有一些注意事项。首先,它不允许通配符*,但我不能确定这一点。我在某个地方读到过,现在找不到那篇文章了。

如果您从不同的域名进行请求,需要添加allow origin头。

Access-Control-Allow-Origin: www.other.com

如果您发出的请求涉及服务器资源,例如POST/PUT/PATCH,并且MIME类型与以下内容不同:application/x-www-form-urlencodedmultipart/form-datatext/plain,则浏览器会自动发出预检OPTIONS请求,以检查服务器是否允许它。
因此,您的API/服务器需要相应地处理这些OPTIONS请求,您需要使用适当的访问控制头进行响应,HTTP响应状态码需要是200
这些标题应该像这样,根据您的需求进行调整:
   Access-Control-Allow-Methods: GET, POST, PUT, PATCH, POST, DELETE, OPTIONS
   Access-Control-Allow-Headers: Content-Type
   Access-Control-Max-Age: 86400

max-age头很重要,在我的情况下,如果没有它,就无法正常工作,我猜测浏览器需要知道“访问权限”有效的时间长度。

此外,如果您正在使用application/json类型的POST请求从不同的域进行请求,您还需要添加先前提到的允许来源头,因此它应该如下所示:

   Access-Control-Allow-Origin: www.other.com
   Access-Control-Allow-Methods: GET, POST, PUT, PATCH, POST, DELETE, OPTIONS
   Access-Control-Allow-Headers: Content-Type
   Access-Control-Max-Age: 86400

当预检请求成功并获取到所有所需信息后,将发出您的实际请求。

一般来说,无论是在初始请求还是预检请求中请求的任何Access-Control头信息,都应该在响应中给出,以使其正常工作。

MDN文档中有一个很好的例子,可以在此链接中找到,您还应该查看这篇Stack Overflow文章


1
这是 Mozilla 的一篇文章,讨论了如何不能在 CORS 来源中使用通配符:链接。所以显然只有在使用凭据时才适用(如果我理解正确的话)。 - Post Impatica
我正在使用通配符并提交Bearer令牌以授权请求,一切都正常,因此不确定我提供的链接是关于凭据的。我的问题是,在.Net Core中构建CORS策略时,我没有添加.AllowCredentials()。 添加.AllowCredentials()后,一切正常运行。 - Post Impatica
“Access-Control-Allow-Origin: www.other.com” 没有任何机会工作,因为 “www.other.com” 不是一个有效的来源值。 - jub0bs

19

如果您正在编写Chrome插件

manifest.json文件中,您需要添加您的域名的权限。

"permissions": [
   "http://example.com/*",
   "https://example.com/*",
   "http://www.example.com/*",
   "https://www.example.com/*"
]

3
请检查您是否有使用"www"前缀。 - Vlas Bashynskyi

16

JavaScript的XMLHttpRequest和Fetch遵循同源策略。因此,使用XMLHttpRequest或Fetch的Web应用程序只能向自己的域名发出HTTP请求。

来源:https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

您必须从服务器端发送Access-Control-Allow-Origin: * HTTP标头。

如果您正在使用Apache作为您的HTTP服务器,则可以像这样将其添加到您的Apache配置文件中:

<IfModule mod_headers.c>
    Header set Access-Control-Allow-Origin "*"
</IfModule>

在Apache中,默认情况下启用了Mod_headers,但您可能希望通过运行以下命令来确保其已启用:

 a2enmod headers

我在哪里可以找到我的Apache配置文件? - Shubham Arya
在Linux Debian中,默认位置是:/etc/apache2/apache2.conf。@ShubhamArya - tedi
在Windows中我可以在哪里找到它? - Shubham Arya
2
@Shubham Arya:Windows默认位置是C:\Program Files\Apache\Apache2\conf\httpd.conf,您可以在http://httpd.apache.org/docs/2.0/platform/windows.xml中找到更多详细信息。 - Kube Kubow

12

在PHP中,您可以添加标题:

<?php
    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Expose-Headers: Content-Length, X-JSON");
    header("Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS");
    header("Access-Control-Allow-Headers: *");
    ...

10

如果你碰巧正在使用IIS服务器,那么你可以在HTTP请求头选项中设置以下标头。

Access-Control-Allow-Origin:*
Access-Control-Allow-Methods: 'HEAD, GET, POST, PUT, PATCH, DELETE'
Access-Control-Allow-Headers: 'Origin, Content-Type, X-Auth-Token';

使用此方法,所有POSTGET等请求方法都可以正常工作。


请注意,在“Access-Control-Allow-Headers”标头中列出“Origin”永远不是必需的。 - jub0bs

10
为了解决Node.js应用程序中的跨域请求问题:
npm i cors

只需将以下行添加到app.js文件中:

let cors = require('cors')
app.use(cors())

6
只有在使用Express JS应用程序时才有效,不适用于所有Node应用程序。 - DrCord
在本地运行时,这些行没有在我的Node JS express应用程序中输出Access-Control-Allow-Origin头。 - EGP

8
在我的Apache虚拟主机配置文件中,我添加了以下行:
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Header always set Access-Control-Max-Age "1000"
Header always set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept, client-security-token"

RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]

当我将这段代码放在cPanel托管服务器的.htaccess文件中时,它对我有效。 - Mazba Uddin
请注意,在“Access-Control-Allow-Headers”头中列出“Origin”永远不是必需的。 - jub0bs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接