Angular CORS 请求无法发送到 Laravel 后端,但预检看起来不错

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27109226/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 10:28:05  来源:igfitidea点击:

Angular CORS requests fail to Laravel backend, but preflight look good

ajaxangularjslaravelcors

提问by Nathan Loding

In short, I'm posting data with Angular to a Laravel backend. The OPTIONS/preflight request looks good, but the subsequent POST fails saying that Access-Control-Allow-Origin header is missing from the requested resource.

简而言之,我将使用 Angular 将数据发布到 Laravel 后端。OPTIONS/预检请求看起来不错,但随后的 POST 失败,说请求的资源中缺少 Access-Control-Allow-Origin 标头。

I'm using Laravel 5 with Angular 1.2.26. Some further documentation on the backend middleware can be found here: https://laracasts.com/discuss/channels/requests/laravel-5-cors-headers-with-filters.

我正在使用带有 Angular 1.2.26 的 Laravel 5。可以在此处找到有关后端中间件的更多文档:https: //laracasts.com/discuss/channels/requests/laravel-5-cors-headers-with-filters

Laravel middleware:

Laravel 中间件:

public function handle($request, Closure $next)
    {
        return $next($request)->header('Access-Control-Allow-Origin' , 'http://laravel.app:8001')
            ->header('Access-Control-Allow-Credentials', 'true')
            ->header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE')
            ->header('Access-Control-Allow-Headers', 'Content-Type, Accept, Authorization, X-Requested-With')
            ->header('Access-Control-Max-Age', '28800');
    }

Angular config - I've tried with various combinations of the commented code, same results each time:

Angular 配置 - 我尝试了注释代码的各种组合,每次都得到相同的结果:

$httpProvider.defaults.useXDomain = true;
    //$httpProvider.defaults.withCredentials = true;
    //delete $httpProvider.defaults.headers.common["X-Requested-With"];
    //$httpProvider.defaults.headers.common["Accept"] = "application/json";
    //$httpProvider.defaults.headers.common["Content-Type"] = "application/json";

Preflight/OPTIONS:

预检/选项:

Remote Address:127.0.0.1:8000
Request URL:http://laravel.app:8000/api/v1/authentication/login
Request Method:OPTIONS
Status Code:200 OK

Request Headers
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:laravel.app:8000
Origin:http://laravel.app:8001
Referer:http://laravel.app:8001/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36

Response Headers
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Content-Type, Accept, Authorization, X-Requested-With
Access-Control-Allow-Methods:POST, GET, OPTIONS, PUT, DELETE
Access-Control-Allow-Origin:http://laravel.app:8001
Access-Control-Max-Age:28800
Allow:GET,HEAD,POST
Cache-Control:no-cache
Connection:keep-alive
Content-Encoding:gzip
Content-Type:text/html; charset=UTF-8
Date:Mon, 24 Nov 2014 16:01:57 GMT
Server:nginx/1.6.2
Set-Cookie:laravel_session=blahblah; expires=Mon, 24-Nov-2014 18:01:57 GMT; Max-Age=7200; path=/; httponly
Set-Cookie:XSRF-TOKEN=blahblah; expires=Thu, 01-Jan-1970 00:02:00 GMT; Max-Age=-1416844797; path=/; httponly
Transfer-Encoding:chunked

POST:

邮政:

Remote Address:127.0.0.1:8000
Request URL:http://laravel.app:8000/api/v1/authentication/login
Request Method:POST
Status Code:500 Internal Server Error

Request Headers
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:47
Content-Type:application/json;charset=UTF-8
Host:laravel.app:8000
Origin:http://laravel.app:8001
Referer:http://laravel.app:8001/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36

Request Payload
{email: "x", password: "x", rememberMe: false}
email: "x"
password: "x"
rememberMe: false

Response Headers
Cache-Control:no-cache
Connection:keep-alive
Content-Type:text/html; charset=UTF-8
Date:Mon, 24 Nov 2014 16:01:57 GMT
Server:nginx/1.6.2
Transfer-Encoding:chunked

回答by richard

After stepping through the VerifyCsrfToken middleware I've established that it was indeed a token mismatch.

在逐步完成 VerifyCsrfToken 中间件之后,我确定它确实是令牌不匹配。

The reason is that Angular was not supplying the CSRF token via header or via a parameter in the post. It worked for GET and OPTIONS requests because these do not validate against the token.

原因是 Angular 没有通过标题或帖子中的参数提供 CSRF 令牌。它适用于 GET 和 OPTIONS 请求,因为它们不会针对令牌进行验证。

So, I looked into Angular and there is documentation on XSRF Protection (see https://docs.angularjs.org/api/ng/service/$http) and lots of discussion out there on how to add the appropriate headers (e.g. https://github.com/angular/angular.js/issues/5122#issuecomment-36157820).

所以,我查看了 Angular 并且有关于 XSRF 保护的文档(参见https://docs.angularjs.org/api/ng/service/$http)以及关于如何添加适当的标题(例如https)的大量讨论://github.com/angular/angular.js/issues/5122#issuecomment-36157820)。

I haven't had a chance to follow any of this up as I have to keep my project moving and my particular use case allows me to get away with disabling VerifyCsrfToken as I only need CORS whilst in development.

我没有机会跟进任何这些,因为我必须保持我的项目继续前进,我的特定用例允许我禁用 VerifyCsrfToken,因为我在开发过程中只需要 CORS。

But hopefully this will give someone else a starting point for solving this issue more fully.

但希望这会给其他人一个更全面解决这个问题的起点。

回答by Nathan Loding

I am unsure if this is an issue with Laravel and the VerifyCsrfToken middleware or not, but the root cause of my issue was that the CSRF token validation was failing. When the error was thrown, the new headers were not included. I'm unclear on the order that these middlewares run in, perhaps that's it, but nonetheless, once I removed the VerifyCsrfToken from the middleware stack, everything lit up.

我不确定这是否是 Laravel 和 VerifyCsrfToken 中间件的问题,但我问题的根本原因是 CSRF 令牌验证失败。抛出错误时,不包括新的标头。我不清楚这些中间件的运行顺序,也许就是这样,但是,一旦我从中间件堆栈中删除了 VerifyCsrfToken,一切都会亮起来。

回答by Rafael F.

I had seme problem. If you use middleware to set Headers, but create a new Response, Response::json(), Response::make(), etc. on controller, this new object don't get the headers set by middleware.

我有一些问题。如果您使用的中间件来设置头,但创建一个新的响应,Response::json()Response::make(),等上控制器,这个新对象没有得到通过中间件设置的标头。

回答by Deepak Kumar T P

I had same problem. In CORS middleware Add the following header.

我有同样的问题。在 CORS 中间件中添加以下标头。

public function handle($request, Closure $next)
{
    //All the domains you want to whitelist
    $trusted_domains = ["http://localhost:4200", "http://127.0.0.1:4200", "http://localhost:3000", "http://127.0.0.1:3000"];
    if (isset($request->server()['HTTP_ORIGIN'])) {
        $origin = $request->server()['HTTP_ORIGIN'];

        if (in_array($origin, $trusted_domains)) {
            header('Access-Control-Allow-Origin: ' . $origin);
            header('Access-Control-Allow-Headers: Origin, Content-Type, Authorization, X-Auth-Token,x-xsrf-token');
            header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE');
        }
    }
    return $next($request);
}