基于Laravel框架--自定义CORS跨域中间件

1.创建中间件

# 1.可以通过php-cli命令行进行创建
php artisan make:middleware CoreHttp
# 2.也可以通过到app/Http/Middleware目录中进行创建

2.配置中间件加载(app/Http/Kernel.php)

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    // 全局中间件加载区
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
    ];

    // 应用分组中间件加载区
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'cors', // todo 此处示例在api应用中进行加载
            'throttle:60,1',
            'bindings',
        ],
    ];
    
    // 应用中间件预加载区
    protected $routeMiddleware = [
        'cors'  =>  \App\Http\Middleware\CoreHttp::class, // 跨域
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    ];
}

3.服务端跨域中间件的代码编写 

<?php

namespace App\Http\Middleware;

use Closure;

class CoreHttp
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';//获取当前域名
        // 非跨域请求
        if (empty($origin)) {
            return $response;
        }
        // todo 允许访问来源域名、允许访问路由(可写入框架配置文件中进行统一管理)
        $allowOrigin = [
            'http://test.laravel.com',
            'http://test.laravel.cn',
        ];
        /*  示例:
            百分百匹配模式 api/user/list 
            模糊匹配模式 api/user/*
        */
        $allowPath = [
            'api/*'
        ];
        // 判断是否为允许访问域名
        if (in_array($origin,$allowOrigin)) {
            $response->header('Access-Control-Allow-Origin', $origin); //允许所有资源跨域

            // 判断是否为可访问路由
            if (!$this->handle_request_paths($allowPath)){
                return false;
            }
        }
        $response->header('Access-Control-Allow-Headers', 'Origin, Content-Type, Cookie, Accept, Authorization, application/json , X-Auth-Token');//允许通过的响应报头
        $response->header('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, OPTIONS, DELETE');//允许的请求方法
        $response->header('Access-Control-Expose-Headers', 'Authorization');//允许axios获取响应头中的Authorization
        $response->header('Allow', 'GET, POST, PATCH, PUT, OPTIONS, delete');//允许的请求方法
        $response->header('Access-Control-Allow-Credentials', 'true');//运行客户端携带证书式访问
        return $response;
    }
    // todo 适用于各种框架调用
    private function handle_request_paths($allowPaths)
    {
        $uri = $_SERVER['REQUEST_URI'];
        $parseUrlInfo = parse_url($uri);
        $path = $parseUrlInfo['path'];
        // 匹配结果标识,默认可匹配
        $isMatch = true;
        if (!empty($allowPaths)) {
            $isMatch = false;
            if ($path !== '/') {
                $path = ltrim($path,'/');
            }
            foreach ($allowPaths as $allowPath) {
                // 不包含全部匹配
                if (!strrpos($allowPath,'/*')) {
                    // 如果访问path与允许的path完全相同,则表示可访问
                    if ($path == $allowPath) {
                        $isMatch = true;
                        break;
                    }
                } else {
                    // 包含全部匹配
                    $allowPath = rtrim($allowPath,'/*');
                    // 如果允许的path能在访问path字符串的最前处找到,则表示可访问
                    if (strpos($path,$allowPath) === 0) {
                        $isMatch = true;
                        break;
                    }
                }
            } unset($allowPath);
        }
        return $isMatch;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_36436407/article/details/108442230