Angular CORS请求未能通过Laravel后端,但预检看起来不错

时间:2014-11-24 16:14:47

标签: ajax angularjs laravel cors

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

我使用Laravel 5和Angular 1.2.26。关于后端中间件的一些进一步文档可以在这里找到:https://laracasts.com/discuss/channels/requests/laravel-5-cors-headers-with-filters

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 - 我尝试过各种评论代码组合,每次都有相同的结果:

$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";

预检/ 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

4 个答案:

答案 0 :(得分:3)

在单步执行VerifyCsrfToken中间件后,我确定它确实是令牌不匹配。

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

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

我没有机会跟随这一切,因为我必须保持我的项目移动,我的特定用例允许我放弃禁用VerifyCsrfToken,因为我在开发过程中只需要CORS。

但希望这会给别人一个解决这个问题的起点。

答案 1 :(得分:2)

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

答案 2 :(得分:0)

我有问题。如果您使用中间件来设置标头,但在控制器上创建新的响应,Response::json()Response::make()等,则此新对象不会获得由中间件设置的标头。

答案 3 :(得分:0)

我有同样的问题。 在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);
}