系統服務化構建-跨域CROS

文本討論關於接口開發中的跨域 CORS。html

CORS是一種瀏覽器協議,源於HTTP 請求的安全策略,在這個體系中的關鍵詞有,同源策略,XMLHttpRequest,Ajax,和先後端分離。尤爲是在目前業界先後端分離的大趨勢下,跨域是一種常見的先後端開發通信(communicate)方式。html5

The basic idea behind CORS is to use custom HTTP headers to allow both the browser and the server to know enough about each other to determine if the request or response should succeed or fail.

以上一段話參考 Cross-domain Ajax with Cross-Origin Resource Sharing/,主要含義是說CORS的核心思想是經過HTTP請求頭通信,使得客戶端和服務器端彼此決定請求和響應是否被成功接受。nginx

Using CORS這裏有一篇關於跨域技術的完整闡述,感興趣的能夠閱讀。git

CORS 協議的實現須要客戶端和服務器端配合協做完成。也就是咱們一般所說的跨域設置。

The browser adds some additional headers, and sometimes makes additional requests, during a CORS request on behalf of the clientgithub

簡單請求

瀏覽器將CORS請求分紅兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。web

Microsoft API設計指導 中這麼一段關於跨域的描述ajax

8.1.1. Avoiding preflightjson

Because the CORS protocol can trigger preflight requests that add additional round trips to the server, performance-critical apps might be interested in avoiding them. The spirit behind CORS is to avoid preflight for any simple cross-domain requests that old non-CORS-capable browsers were able to make. All other requests require preflight.後端

A request is "simple" and avoids preflight if its method is GET, HEAD or POST, and if it doesn't contain any request headers besides Accept, Accept-Language and Content-Language. For POST requests, the Content-Type header is also allowed, but only if its value is "application/x-www-form-urlencoded," "multipart/form-data" or "text/plain." For any other headers or values, a preflight request will happen.api

The spirit behind CORS is to avoid preflight for any simple cross-domain requests that old non-CORS-capable browsers were able to make

咱們提煉出簡單請求的判斷標準

1 )GET, HEAD or POST 三種請求
2) 不增長任何額外的請求頭
3) POST 請求容許三種Content—Type: "application/x-www-form-urlencoded," "multipart/form-data" or "text/plain.

非簡單請求

不符合簡單請求的,均屬於非簡單請求。這麼看來咱們平時常常使用的

Content-Type: application/json

顯然是一種非簡單請求頭。

對於非簡單請求,CORS 機制會自動觸發瀏覽器首先進行 preflight(一個 OPTIONS 請求), 該請求成功後纔會發送真正的請求。這裏的 preflight 也能夠被翻譯爲(預檢)請求。

CROS 流程.png

Access-Control-Allow-Headers

像上文中所說那樣, 增長了自定義字段後,跨域請求就變成了一種帶有 preflight 的非簡單請求,所以會有下面的一種理解。

Access-Control-Allow-Headers是 preflight 請求中用來標識真正請求將會包含哪些頭部字段,也就是下文中的自定義頭部。這種方式是服務器端安全防範的一種。

Access-Control-Allow-Credentials

這個設置是關因而否支持Cookies的

xhr.withCredentials = true;

Access-Control-Allow-Credentials: true

YII2 中的跨域設置

//Access-Control-Allow-Origin:*
    public $arr_acao = [
        '*'
    ];
    //Access-Control-Allow-Methods
    public $arr_acam = [
        'POST', 
        'PUT', 
        'GET', 
        'DELETE',
        'OPTIONS'
    ];
    //Access-Control-Allow-Headers
    public $arr_acah = [
        'token', 
        'app-key',
        'content-type',
    ];

 header('Access-Control-Allow-Origin: '.implode(',', $this->arr_acao));
        header('Access-Control-Allow-Methods: '.implode(',', $this->arr_acam));
        header('Access-Control-Allow-Headers: '.implode(',', $this->arr_acah));
        header("Access-Control-Max-Age: 86400");

        if ($req->isOptions) {
            $code = "202";
            $message = "Accepted";
            header("HTTP/1.1 ".$code." ".$message); 
            exit();
        }

經過 Access-Control-Allow-Headers 設置自定義字段的方式,是一種安全策略,服務端要求請求頭必須攜帶 'token', 'app-key','content-type'三個屬性字段,缺一不可,不然請求不能達成。

origin 'http://xx.cn' has been blocked by CORS policy:
Request header field timestamp is not allowed by Access-Control-Allow-Headers in preflight response.

Nginx中的跨域

location ~* \.(eot|ttf|woff|woff2|svg)$ {
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Headers X-Requested-With;
    add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
}

以上設置的前提是Nginix 開啓了模塊 ngx_http_headers_module

Nginx中的其它CORS配置

#
# 用於nginx的開放式CORS配置
#
location / {
     if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        #
        # 自定義標題和標題各類瀏覽器*應該*能夠,但不是
        #
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        #
        # 有效期爲
        #
        add_header 'Access-Control-Max-Age' 22000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
     }
     if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }

Nginx中跨域,應用程序與web服務器存在耦合,增長了應用程序部署和擴展的複雜性,按需使用。

CROS 總結

本文主要介紹了CROS的基本分類和常見的實現方案,對於同源策略,XMLHttpRequest請求等基礎知識被沒有過多涉及。簡單請求和非簡單請求的分類是重點。理解了這一點,就能理解什麼場景瀏覽器會發起預檢請求,並回復對應的響應。

咱們常說跨域設置是客戶端和服務器端一塊兒配合的結果,官方協議更傾向於讓開發者對於跨域無感知,而瀏覽器與後端服務的交互和相互信任是核心。

參考資料

I want to add CORS support to my server
Using CORS


圖南日晟.jpg

相關文章
相關標籤/搜索