場景描述
最近在做公司的一個項目,我們在做這個小項目的時候,決定采用三端分離,數據庫、服務器端、前端都分離。這樣的分離是好的,但是我們遇到了一個問題,這個問題就是跨域問題和預請求問題。跨域的問題是因為我們將前端和后端放在兩台服務器,數據之間的訪問是通過RestApi進行的,這個時候的訪問就涉及跨域的問題。還有一個問題就是預請求的問題,這個問題的引發就是在訪問Api的時候要攜帶令牌,沒有令牌是不能訪問的。前端每次在訪問接口的時候都要在請求頭加上Token,加上token之后就出現了預請求的問題。為了解決預請求太搞腦子。下面我們就來說說預請求的問題。
CORS描述
一、CORS定義
跨來源資源共享(CORS)是一份瀏覽器技術的規范,提供了 Web 服務從不同網域傳來沙盒腳本的方法,以避開瀏覽器的同源策略,是 JSONP 模式的現代版。與 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以讓網頁設計師用一般的 XMLHttpRequest,這種方式的錯誤處理比JSONP要來的好,JSONP對於 RESTful 的 API 來說,發送 POST/PUT/DELET 請求將成為問題,不利於接口的統一。但另一方面,JSONP 可以在不支持 CORS 的老舊瀏覽器上運作。不過現代的瀏覽器(IE10以上)基本都支持 CORS。
二、簡單請求和非簡單請求
只要同時滿足以下兩大條件,就屬於簡單請求。否側就是非簡單請求。
(1) 請求方法是以下三種方法之一: HEAD GET POST (2)HTTP的頭信息不超出以下幾種字段: Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
對於簡單請求,瀏覽器直接發出CORS請求。具體來說,就是在頭信息之中,增加一個Origin
字段。
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是PUT
或DELETE
,或者Content-Type
字段的類型是application/json或者自定義請求頭信息
。
非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。
非簡單請求兩個重要的參數:
1、Access-Control-Request-Method:該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法。
2、Access-Control-Request-Headers:該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段。
三、預檢請求定義
在 CORS 中,可以使用 OPTIONS 方法發起一個預檢請求(一般都是瀏覽檢測到請求跨域時,會自動發起),以檢測實際請求是否可以被服務器所接受。預檢請求報文中的 Access-Control-Request-Method 首部字段告知服務器實際請求所使用的 HTTP 方法;Access-Control-Request-Headers 首部字段告知服務器實際請求所攜帶的自定義首部字段。服務器基於從預檢請求獲得的信息來判斷,是否接受接下來的實際請求。
預檢請求的過程
由上面圖可以看出,解決預請求是靠服務器來解決。
請求失敗的結果:如果失敗請求頭的Token就不能加上去
請求成功的結果
laravel怎么實現OPTIONS請求
我在網上找了好多資料,看了之后都不能解決預檢測的問題,因為項目的需要,需要在所有的Api接口的訪問攜帶Token,這個時候我們注冊中間件就要注冊全局的,而不是路由的。
注意:我第一次在路由里面直接注冊中間件,還是不能成功響應。通過調試發現Laravel預檢測不能放在路由的中間件里面,如果放在哪就不生效。我們要放在全局中間件里面或者直接放在路由里面。
見代碼
public function handle($request, Closure $next)
{
$response = $next($request);
$response->header('Access-Control-Allow-Origin', '*');
$response->header('Access-Control-Allow-Headers', 'Origin,No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, token');
$response->header('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, OPTIONS');
$response->header('Access-Control-Allow-Credentials', 'true');
//驗證token是否
$token = $request->header('token');
if(!$token){
return Base::response('授權失敗,請檢查token',0);
}else{
$res = (new Token())->verifyToken($token);
if(!$res){
return Base::response('token失效,請重新獲取',0);
}
}
return $response;
}
注冊中間件
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\CorsHttp::class,
];
最后大功告成,希望可以幫助到大家。