人在江湖走,哪有不湿鞋?

经常做api开发的的时候,总会遇到跨域的情况,特别是前后端完全分离的情况,为什么会这样呢?

出于安全性的原因,浏览器会限制 Script 中的跨域请求。由于 XMLHttpRequest 遵循同源策略,所有使用 XMLHttpRequest 构造 HTTP 请求的应用只能访问自己的域名,如果需要构造跨域的请求,那么开发者需要配合浏览器做出一些允许跨域的配置。

解决办法有很多,主要说下两种:

方法之一,修改nginx

修改web服务器的配置允许跨域,一劳永逸,以nginx为例:

在站点配置节点下添加:

server
{
    listen       80;
    index index.html index.htm index.php;
    root  /www/public/;

    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers "x-requested-with,Authorization,Accept, Origin, X-Requested-With, Content-Type, Last-Modified";
    add_header Access-Control-Allow-Methods GET,POST;
    add_header Access-Control-Max-Age 1728000;

    # ....
}

方法之二,在php上做手脚

虽然修改nginx配置是挺方便,让你懒到不用改一行代码,但:

不够灵活,不能针对某个接口去允许跨域,只能全局设置,不够安全;

条件不一定允许,很多时候不一定拥有nignx的修改权限。

这里说下laravel框架里设置跨域:

首先,您需要一个中间件:

<?php
namespace App\Http\Middleware;

use Closure;

class EnableCrossRequest
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $origin     = $request->server('HTTP_ORIGIN') ? $request->server('HTTP_ORIGIN') : '';
        $originHost = parse_url($origin, PHP_URL_HOST);
        $originPort = parse_url($origin, PHP_URL_PORT);
        $originHost .= $originPort ? ':' . $originPort : '';

        // 允许跨域的域名 可以加在配置里
        $allowOriginHost = [
            'test.com',
        ];

        $response = $next($request);

        if (in_array($originHost, $allowOriginHost)) {
            $response->header('Access-Control-Allow-Origin', $origin);
            $response->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Cookie, X-CSRF-TOKEN, Accept, Authorization, X-XSRF-TOKEN, Last-Modified');
            $response->header('Access-Control-Expose-Headers', 'Authorization, authenticated');
            $response->header('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, OPTIONS');
            $response->header('Access-Control-Allow-Credentials', 'true');
        }

        return $response;
    }
}

然后注册中间件,app/Http/Kernel.php


    protected $middlewareGroups = [
        // ....
        'api.cross' => [
            \App\Http\Middleware\EnableCrossRequest::class,
        ],
        // ....
    ];

然后,在需要跨域的路由上加上该中间件

Route::group([
    'prefix'     => 'api',
    'middleware' => ['api.cross'],
], function () {
    // 路由    
});

解决axios跨域时,发送post请求变options的问题

跨域遇到复杂请求时, 会先发送options去试探是否支持跨域, 所以有两次请求完全正常,但会导致程序重复处理,所以可以通过以下方法屏蔽options请求

Route::options('/{all}', function () {
    return response('');
})->where(['all' => '([a-zA-Z0-9-]|_|/)+']);