Nginx通過CORS實現跨域

wufn0087 8年前發布 | 36K 次閱讀 Nginx Web服務器

什么是CORS

CORS是一個W3C標準,全稱是跨域資源共享(Cross-origin resource sharing)。它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。

當前幾乎所有的瀏覽器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)都可通過名為跨域資源共享(Cross-Origin Resource Sharing)的協議支持AJAX跨域調用。

Chrome,Firefox,Opera,Safari都使用的是XMLHttpRequest2對象,IE使用XDomainRequest。

簡單來說就是跨域的目標服務器要返回一系列的Headers,通過這些Headers來控制是否同意跨域。跨域資源共享(CORS)也是未來的跨域問題的標準解決方案。

CORS提供如下Headers,Request包和Response包中都有一部分。

HTTP Response Header

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Credentials
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Expose-Headers
  • Access-Control-Max-Age

HTTP Request Header

  • Access-Control-Request-Method
  • Access-Control-Request-Headers

其中最敏感的就是Access-Control-Allow-Origin這個Header, 它是W3C標準里用來檢查該跨域請求是否可以被通過。(Access Control Check)。如果需要跨域,解決方法就是在資源的頭中加入Access-Control-Allow-Origin 指定你授權的域。

啟用CORS請求

假設您的應用已經在example.com上了,而您想要從www.example2.com提取數據。一般情況下,如果您嘗試進行這種類型的AJAX調用,請求將會失敗,而瀏覽器將會出現源不匹配的錯誤。利用CORS后只需www.example2.com 服務端添加一個HTTP Response頭,就可以允許來自example.com的請求。

將Access-Control-Allow-Origin添加到某網站下或整個域中的單個資源

Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Credentials: true (可選)

將允許任何域向您提交請求

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true (可選)

提交跨域請求

如果服務器端已啟用了CORS,那么提交跨域請求就和普通的XMLHttpRequest請求沒什么區別。例如現在example.com可以向www.example2.com提交請求。

var xhr = new XMLHttpRequest();
// xhr.withCredentials = true; //如果需要Cookie等
xhr.open('GET', 'http://www.example2.com/hello.json');
xhr.onload = function(e) {
  var data = JSON.parse(this.response);
  ...
}
xhr.send();

服務端Nginx配置

要實現CORS跨域,服務端需要下圖中這樣一個流程

  • 對于簡單請求,如GET,只需要在HTTP Response后添加Access-Control-Allow-Origin。
  • 對于非簡單請求,比如POST、PUT、DELETE等,瀏覽器會分兩次應答。第一次preflight(method: OPTIONS),主要驗證來源是否合法,并返回允許的Header等。第二次才是真正的HTTP應答。所以服務器必須處理OPTIONS應答。

流程如下

  • 首先查看http頭部有無origin字段;
  • 如果沒有,或者不允許,直接當成普通請求處理,結束;
  • 如果有并且是允許的,那么再看是否是preflight(method=OPTIONS);
  • 如果是preflight,就返回Allow-Headers、Allow-Methods等,內容為空;
  • 如果不是preflight,就返回Allow-Origin、Allow-Credentials等,并返回正常內容。

用偽代碼表示

location /pub/(.+) {
    if ($http_origin ~ <允許的域(正則匹配)>) {
        add_header 'Access-Control-Allow-Origin' "$http_origin";
        add_header 'Access-Control-Allow-Credentials' "true";
        if ($request_method = "OPTIONS") {
            add_header 'Access-Control-Max-Age' 86400;
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE';
            add_header 'Access-Control-Allow-Headers' 'reqid, nid, host, x-real-ip, x-forwarded-ip, event-type, event-id, accept, content-type';
            add_header 'Content-Length' 0;
            add_header 'Content-Type' 'text/plain, charset=utf-8';
            return 204;
        }
    }

# 正常nginx配置
......

}</code></pre>

Nginx配置實例

實例一:允許example.com的應用在www.example2.com上跨域提取數據

在nginx.conf里找到server項,并在里面添加如下配置

location /{

add_header 'Access-Control-Allow-Origin' '如果需要允許來自任何域的訪問,可以這樣配置

add_header Access-Control-Allow-Origin *;

注釋如下

第一條指令:授權從example.com的請求(必需)

第二條指令:當該標志為真時,響應于該請求是否可以被暴露(可選)

第三條指令:允許腳本訪問的返回頭(可選)

第四條指令:指定請求的方法,可以是GET, POST, OPTIONS, PUT, DELETE等(可選)

重啟Nginx

$ service nginx reload

測試跨域請求

$ curl -I -X OPTIONS -H "Origin: http://example.com" http://www.example2.com

成功時,響應頭是如下所示

HTTP/1.1 200 OK
Server: nginx
Access-Control-Allow-Origin: example.com

實例二:Nginx允許多個域名跨域訪問

由于Access-Control-Allow-Origin參數只允許配置單個域名或者 * ,當我們需要允許多個域名跨域訪問時可以用以下幾種方法來實現。

  • 方法一

如需要允許用戶請求來自www.example.com、m.example.com、wap.example.com訪問www.example2.com域名時,返回頭Access-Control-Allow-Origin,具體配置如下

在nginx.conf里面,找到server項,并在里面添加如下配置

map $http_origin $corsHost {
    default 0;
    "~http://www.example.com;
    "~http://m.example.com;
    "~http://wap.example.com;
}

server { listen 80; server_name www.example2.com; root /usr/share/nginx/html; location / { add_header Access-Control-Allow-Origin $corsHost; } }</code></pre>

  • 方法二

如需要允許用戶請求來自localhost、www.example.com或m.example.com的請求訪問xxx.example2.com域名時,返回頭Access-Control-Allow-Origin,具體配置如下

在Nginx配置文件中xxx.example2.com域名的location /下配置以下內容

set $cors '';
if ($http_origin ~* 'https?://(localhost|www.example.com|m.example.com)') {
        set $cors 'true';
}

if ($cors = 'true') { add_header 'Access-Control-Allow-Origin' "$http_origin"; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Mx-ReqToken,X-Requested-With'; }

if ($request_method = 'OPTIONS') { return 204; }</code></pre>

  • 方法三

如需要允許用戶請求來自*.example.com訪問xxx.example2.com域名時,返回頭Access-Control-Allow-Origin,具體配置如下

在Nginx配置文件中xxx.example2.com域名的location /下配置以下內容

if ( $http_origin ~ http://(.*).example.com){
         set $allow_url $http_origin;
    }

#CORS(Cross Orign Resource-Sharing)跨域控制配置
#是否允許請求帶有驗證信息
add_header Access-Control-Allow-Credentials true;
#允許跨域訪問的域名,可以是一個域的列表,也可以是通配符*
add_header Access-Control-Allow-Origin $allow_url;
#允許腳本訪問的返回頭
add_header Access-Control-Allow-Headers 'x-requested-with,content-type,Cache-Control,Pragma,Date,x-timestamp';
#允許使用的請求方法,以逗號隔開
add_header Access-Control-Allow-Methods 'POST,GET,OPTIONS,PUT,DELETE';
#允許自定義的頭部,以逗號隔開,大小寫不敏感
add_header Access-Control-Expose-Headers 'WWW-Authenticate,Server-Authorization';
#P3P支持跨域cookie操作
add_header P3P 'policyref="/w3c/p3p.xml", CP="NOI DSP PSAa OUR BUS IND ONL UNI COM NAV INT LOC"';</code></pre> 

  • 方法四

如需要允許用戶請求來自xxx1.example.com或xxx1.example1.com訪問xxx.example2.com域名時,返回頭Access-Control-Allow-Origin,具體配置如下

在Nginx配置文件中xxx.example2.com域名的location /下配置以下內容

location / {

if ( $http_origin ~ .*.(example|example1).com ) {
add_header Access-Control-Allow-Origin $http_origin;
}

}</code></pre>

實例三:Nginx跨域配置并支持DELETE,PUT請求

默認Access-Control-Allow-Origin開啟跨域請求只支持GET、HEAD、POST、OPTIONS請求,使用DELETE發起跨域請求時,瀏覽器出于安全考慮會先發起OPTIONS請求,服務器端接收到的請求方式就變成了OPTIONS,所以引起了服務器的405 Method Not Allowed。

解決方法

首先要對OPTIONS請求進行處理

if ($request_method = 'OPTIONS') { 
    add_header Access-Control-Allow-Origin *; 
    add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;

#其他頭部信息配置,省略...
return 204; 

}</code></pre>

當請求方式為OPTIONS時設置Allow的響應頭,重新處理這次請求。這樣發出請求時第一次是OPTIONS請求,第二次才是DELETE請求。

# 完整配置參考

將配置文件的放到對應的server {}里

add_header Access-Control-Allow-Origin *;

location / { if ($request_method = 'OPTIONS') { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS; return 204; } index index.php; try_files $uri @rewriteapp; }</code></pre>

實例四:更多配置示例

  • 示例一
The following Nginx configuration enables CORS, with support for preflight requests.

#

Wide-open CORS config for nginx

# location / { if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; #

    # Custom headers and headers various browsers *should* be OK with but aren't
    #
    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
    #
    # Tell client that this pre-flight info is valid for 20 days
    #
    add_header 'Access-Control-Max-Age' 1728000;
    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,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
 }
 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,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
 }

}</code></pre>

  • 示例二
if ($request_method = 'OPTIONS') {  
    add_header 'Access-Control-Allow-Origin' 'https://docs.domain.com';  
    add_header 'Access-Control-Allow-Credentials' 'true';  
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS';  
    add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,token';  
    return 204;
}
if ($request_method = 'POST') {  
    add_header 'Access-Control-Allow-Origin' 'https://docs.domain.com';  
    add_header 'Access-Control-Allow-Credentials' 'true';  
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS';  
    add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,token';  
}  
if ($request_method = 'GET') {  
    add_header 'Access-Control-Allow-Origin' 'https://docs.domain.com';  
    add_header 'Access-Control-Allow-Credentials' 'true';  
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS';  
    add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,token';  
}

其它技巧

Apache中啟用CORS

在httpd配置或.htaccess文件中添加如下語句

SetEnvIf Origin "^(.*\.example\.com)$" ORIGIN_SUB_DOMAIN=$1  
Header set Access-Control-Allow-Origin "%{ORIGIN_SUB_DOMAIN}e" env=ORIGIN_SUB_DOMAIN

PHP中啟用CORS

通過在服務端設置Access-Control-Allow-Origin響應頭

  • 允許所有來源訪問
<?php
header("Access-Control-Allow-Origin: *");
?>
  • 允許來自特定源的訪問
<?php
header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
?>
  • 配置多個訪問源

由于瀏覽器實現只支持了單個origin、*、null,如果要配置多個訪問源,可以在代碼中處理如下

<?php
$allowed_origins   = array(  
                            "http://www.example.com"   ,  
                            "http://app.example.com"  ,  
                            "http://cms.example.com"  ,  
                          );  
if (in_array($_SERVER['HTTP_ORIGIN'], $allowed_origins)){    
    @header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);  
}
?>

HTML中啟用CORS

<meta http-equiv="Access-Control-Allow-Origin" content="*">

參考文檔

http://www.google.com

http://t.cn/RZEYPmD

http://t.cn/RhcAN2d

http://to-u.xyz/2016/06/30/nginx-cors/

http://coderq.github.io/2016/05/13/cross-domain/

 

 

來自:http://www.yunweipai.com/archives/9381.html

 

 本文由用戶 wufn0087 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!