1. 程式人生 > >nginx通過CORS實現跨域

nginx通過CORS實現跨域

https://www.cnblogs.com/sunmmi/articles/5956554.html

1.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包中都有一部分。

2.HTTP Response Header

1

2

3

4

5

6

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

1

2

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新增到某網站下或整個域中的單個資源

1

2

Access-Control-Allow-Origin: http://example.com

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

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

1

2

Access-Control-Allow-Origin: *

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

3.提交跨域請求

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

1

2

3

4

5

6

7

8

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();選)

  • 對於簡單請求,如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等,並返回正常內容。

用偽程式碼表示

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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配置

    ......

}

Nginx配置例項

例項一:允許example.com的應用在www.example2.com上跨域提取資料

在nginx.conf裡找到server項,並在裡面新增如下配置

1

2

3

4

5

6

7

8

location /{

 

add_header 'Access-Control-Allow-Origin' 'http://example.com';

add_header 'Access-Control-Allow-Credentials' 'true';

add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With';

add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';

...

}

如果需要允許來自任何域的訪問,可以這樣配置

1

add_header Access-Control-Allow-Origin *;

註釋如下

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

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

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

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

重啟Nginx

1

$ service nginx reload

測試跨域請求

1

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

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

1

2

3

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項,並在裡面新增如下配置

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

map $http_origin $corsHost {

    default 0;

    "~http://www.example.com" http://www.example.com;

    "~http://m.example.com" http://m.example.com;

    "~http://wap.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;

    }

}

  • 方法二

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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;

}

  • 方法三

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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"';

  • 方法四

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

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

1

2

3

4

5

6

location / {

 

    if ( $http_origin ~ .*.(example|example1).com ) {

    add_header Access-Control-Allow-Origin $http_origin;

    }

}

例項三:Nginx跨域配置並支援DELETE,PUT請求

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

解決方法

首先要對OPTIONS請求進行處理

1

2

3

4

5

6

if ($request_method = 'OPTIONS') {

    add_header Access-Control-Allow-Origin *;

    add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;

    #其他頭部資訊配置,省略...

    return 204;

}

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

# 完整配置參考

# 將配置檔案的放到對應的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;

}

例項四:更多配置示例

  • 示例一

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

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';

     }

}

  • 示例二

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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檔案中新增如下語句

1

2

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響應頭

  • 允許所有來源訪問

1

2

3

<?php

header("Access-Control-Allow-Origin: *");

?>

  • 允許來自特定源的訪問

1

2

3

<?php

header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);

?>

  • 配置多個訪問源

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

1

2

3

4

5

6

7

8

9

10

<?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

1

<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