Building CORS APIs with Lumen

PHP:7.2

Lumen:6.3

Lumen 是一個基於 Laravel 的微型框架,最常應用在建立 API Service 上面。

隨著軟體技術的演進,越來越多網站專案都是走 SPA(Single Page Application) 架構,因此建立 CORS(Cross-Origin Resource Sharing)API 也漸漸地變成很常見的需求。

但是考量到安全性的問題,跨來源資源共用(CORS) 就會多出了許多規範,因此在 API 設計上就必須再特別處理 request 的部份。

Preflighted(預檢請求)

在開始之前,有一些 HTTP 的運作方式需要先瞭解。

由於跨站請求可能會攜帶使用者資料,所以要先進行預檢請求,因此在 CORS 的狀況之下,只要滿足 PUTDELETEPOST 等請求,會先以 OPTIONS 向另一個 domain 送出 預檢請求(Preflighted),以確認後續請求是否可安全送出。

HTTP OPTIONS

因此在設計 API 時,就必須多考量到這個部份。

Set up a CORS middleware

為了針對所有的 request 進行調整,因此我們可以在 app\Http\Middleware 建立一個 Middleware,讓所有的 request 都可以套用。

app/Http/Middleware/CorsMiddleware.php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Response;

class CorsMiddleware
{
    // 允許要求的 Origin
    protected $allowOrigin = [
        'http://localhost'
    ];

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $origin = $request->header('Origin');

        // 判斷是否為合法來源
        $isAllowOrigin = in_array($origin, $this->allowOrigin);

        // 判斷是否為 HTTP OPTIONS method
        $isOptions = $request->isMethod('OPTIONS');

        if (!$isAllowOrigin && $isOptions) {
            // 非法來源
            return new Response('', Response::HTTP_FORBIDDEN);
        }

        if ($isOptions) {
            // 合法來源的預檢請求
            $response = new Response('', Response::HTTP_OK);
        } else {
            $response = $next($request);
        }

        // 設定 Header
        return $response->withHeaders([
            'Access-Control-Allow-Origin' => $origin,
            'Access-Control-Allow-Methods' => '*',
            'Access-Control-Allow-Headers' => 'Content-Type, X-Requested-With, Authorization',
        ]);
    }
}

Set up routes

在設定好 Middleware 以後,要再設定 route 才能套用到所有的 endpoint

route/web.php

// 由於 HTTP 會送出 OPTIONS 的請求,因此還是需要 handle OPTIONS 的 request
$router->options('{any:.*}', ['middleware' => 'cors']);

// 綁定 CORS Middleware
$router->group(['middleware' => 'cors'], function () use ($router) {
    $router->get('info', 'TestController@getInfo');
    $router->post('update', 'TestController@updateInfo');
});

這樣就大功告成了。

Categories: Lumen