问题场景

我司今日针对B端管理后台做Dashboard升级,采用新分离式开发方式,Laravel提供 API、Vue做视图,自然也遇到了老生常谈跨域问题。

问题原因

出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。

{% blockquote %} CORS:跨域资源共享(CORS) 是一种机制,他使用额外的HTTP头来告诉浏览器,让运行在一个origin(domain)上的WEB应用被准许访问来自不同源服务器上指定的资源。 {% endblockquote %}

当一个资源从与该资源本身所在的服务器不同的域协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

比如,站点 http://domain-a.com 的某 HTML 页面通过 <img> 的 src 请求 http://domain-b.com/image.jpg

CORS_principle

解决方案

  1. 针对Laravel框架下有现成扩展包可用,如:barryvdh/laravel-cors,使用方法自行阅读文档(亲测过)。

  2. 奈何自个公司Laravel框架版本是5.3,上面方法行不通,升级框架不是目前所要做的事情,弄明白原理后自行编写中间件也可以搞定,一句话总结:即需要在响应Header头Access-Control-Allow-Origin:*设置允许的域,这里的*表示允许所有域。

需要注意的是:如果客户端(浏览器)需要携带身份请求,那么也需要服务端在响应中将Header头Access-Control-Allow-Credentials字段设置为true,完整写法Access-Control-Allow-Credentials: true,重点来了(敲黑板划重点)此时的Access-Control-Allow-Origin就不能再设置为*了,必须指定具体的域,比如:http://foo.example,这个域一般是前端开发者本地的http://localhost:9527

代码

  1. 创建中间件php artisan make:middleware EnableCrosMiddleware

  2. 核心代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        $origin = $request->server('HTTP_ORIGIN') ? $request->server('HTTP_ORIGIN') : '';
        $allowOrigin = [
            'http://localhost:9527',
            'http://localhost:9690',
            'http://10.0.1.38:9527',
            'http://10.0.1.38:9690'
        ];

        if (in_array($origin, $allowOrigin)) {
            $response->header('Access-Control-Allow-Origin', $origin);
            $response->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Cookie, Accept, X-XSRF-TOKEN');
            $response->header('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, OPTIONS');
            $response->header('Access-Control-Allow-Credentials', 'true');
        }

        return $response;
    }
  1. 添加到全局路由组,打开app/Http/Kernel.php,在protected $middleware属性中添加上刚刚创建中间件类,我的添加行是\Fujitaro\Http\Middleware\EnableCrosMiddleware::class,(不要照抄咱们的命名空间肯定不同)当然你也可以只添加到api分组下

常见问题

Q1:跨域错误提示

cors

A1:检查服务端响应头HeaderAccess-Control-Allow-Origin的设置,应该是没有成功

Q2:非跨域错误提示

cors

A2:检查服务端Access-Control-Allow-Headers中是否允许x-xsrf-token,设置允许即可。

参考