天天看點

前端通過Nginx反向代了解決跨域問題

在前面寫的一篇文章SpringMVC解決跨域問題,我們探讨了什麼是跨域問題以及SpringMVC怎麼解決跨域問題,解決方式主要有如下三種方式:

  1. JSONP
  2. CORS
  3. WebSocket

可是這幾種方式都是基于伺服器配置的,即對于自己的網站是可以通過這幾種方式解決的,可是現在遇到另一個需求(前面提到過,寫扇貝插件,我們不能更改扇貝的伺服器配置,也不能發短信叫他們給我配置一下)。

本文探讨了前端如何通過Nginx反向代理的方式解決跨域問題。

跨域

再次重申: 跨域是浏覽器行為,不是伺服器行為。

實際上,請求已經到達伺服器了,隻不過在回來的時候被浏覽器限制了。就像Python他可以進行抓取資料一樣,不經過浏覽器而發起請求是可以得到資料,想到通過Nginx的反向代理來解決跨域問題。

代理

所謂代理就是在我們和真實的伺服器之間有一台代理伺服器,我們所有的請求都是通過它來進行轉接的。

正向代理

正向代理就是我們通路不了Google,但是我在國外有一台vps,它可以通路Google,我通路它,叫它通路Google後,把資料傳給我。

如圖:

前端通過Nginx反向代了解決跨域問題

反向代理

大家都有過這樣的經曆,撥打10086客服電話,可能一個地區的10086客服有幾個或者幾十個,你永遠都不需要關心在電話那頭的是哪一個,叫什麼,男的,還是女的,漂亮的還是帥氣的,你都不關心,你關心的是你的問題能不能得到專業的解答,你隻需要撥通了10086的總機号碼,電話那頭總會有人會回答你,隻是有時慢有時快而已。那麼這裡的10086總機号碼就是我們說的反向代理。客戶不知道真正提供服務人的是誰。

反向代理隐藏了真實的服務端,當我們請求 www.baidu.com 的時候,就像撥打10086一樣,背後可能有成千上萬台伺服器為我們服務,但具體是哪一台,你不知道,也不需要知道,你隻需要知道反向代理伺服器是誰就好了,www.baidu.com 就是我們的反向代理伺服器,反向代理伺服器會幫我們把請求轉發到真實的伺服器那裡去。Nginx就是性能非常好的反向代理伺服器,用來做負載均衡。

前端通過Nginx反向代了解決跨域問題

總結

  • 正向代理隐藏了真實的用戶端。
  • 反向代理隐藏了真實的伺服器。

Nginx 就是一個很好的反向代理伺服器,當然apache也可以實作此功能。

windows下Apache配置參考這篇文章: Windows Apache伺服器配置

Nginx

Nginx(發音同engine x)是一個 Web伺服器,也可以用作反向代理,負載平衡器和 HTTP緩存。該軟體由 Igor Sysoev 建立,并于2004年首次公開釋出。同名公司成立于2011年,以提供支援。

我在Windows下實作Nginx負載均衡提到過Windows下Nginx指令使用。

Nginx 反向代理子產品 proxy_pass

proxy_pass

後面跟着一個 URL,用來将請求反向代理到 URL 參數指定的伺服器上。例如我們上面例子中的

proxy_pass https://api.shanbay.com

,則将比對的請求反向代理到 https://api.shanbay.com。

通過在配置檔案中增加

proxy_pass 你的伺服器ip

,例如這裡的扇貝伺服器位址,就可以完成反向代理。

server {
        listen       80;
        server_name  localhost;
        ## 使用者通路 localhost,則反向代理到https://api.shanbay.com
        location / {
            root   html;
            index  index.html index.htm;
            proxy_pass https://api.shanbay.com;
        }
}
           

配置html以檔案方式打開

一般的情況下,我們的HTML檔案時放置在Nginx伺服器上面的,即通過輸入 http://localhost/index.html ,但是在前端進行調試的時候,我們可能是通過 使用 file:///E:/nginx/html/index.html 來打開HTML。伺服器打開不是特别友善。

而我們之是以要部署在伺服器上,是想要使用浏覽器自帶的CORS頭來解決跨域問題,如果不想把HTML放置在Nginx中,而想通過本地打開的方式來調試HTML,可以通過自己添加

Access-Control-Allow-Origin

等http頭,但是我們的AJAX請求一定要加上

http://127.0.0.1/request

,而不能直接是

/request

,于是将nginx.conf作如下配置:

location / {
    root   html;
    index  index.html index.htm;
    # 配置html以檔案方式打開
    if ($request_method = 'POST') {
          add_header 'Access-Control-Allow-Origin' *;
          add_header 'Access-Control-Allow-Credentials' 'true';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, 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';
      }
    if ($request_method = 'GET') {
          add_header 'Access-Control-Allow-Origin' *;
          add_header 'Access-Control-Allow-Credentials' 'true';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, 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';
    }
    # 代理到8080端口
    proxy_pass        http://127.0.0.1:8080;

}
           

處理DELETE和PUT跨域請求

而現在我的背景是restful風格的接口,采用了delete和put方法,而上面的配置就無能為力了。

可以通過增加對非簡單請求的判斷來解決DELETE和PUT跨域請求。

非簡單請求是那種對伺服器有特殊要求的請求,比如請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json。

非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。

伺服器收到"預檢"請求以後,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以後,确認允許跨源請求,就可以做出回應。

是以,為了使Nginx可以處理delete等非簡單請求,Nginx需要作出相應的改變,更改配置如下

location / {
    # 完成浏覽器的"預檢"請求
		if ($request_method = 'OPTIONS') {
		add_header Access-Control-Allow-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' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
		return 204;
		}
    # 配置html在本地打開
    if ($request_method = 'POST') {
          add_header 'Access-Control-Allow-Origin' *;
          add_header 'Access-Control-Allow-Credentials' 'true';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, 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';
      }
    if ($request_method = 'GET') {
          add_header 'Access-Control-Allow-Origin' *;
          add_header 'Access-Control-Allow-Credentials' 'true';
          add_header 'Access-Control-Allow-Methods' 'GET, POST, 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';
    }
    root   html;
    index  index.html index.htm;
    # 配置html在Nginx中打開
    location ~* \.(html|htm)$ {

		}
    # 代理到8080端口
    proxy_pass        http://127.0.0.1:8080;

}
           

我們還必須把我們的html代碼放在Nginx中html檔案夾内,即使用Nginx當做我們的前端伺服器。

URL重寫

有時候我們僅僅隻想将

/api

下的url反向代理到後端,可以通過在nginx.conf中配置url重寫規則如下:

location / {
    root   html;
    index  index.html index.htm;
         location ^~ /api {
         rewrite ^/api/(.*)$ /$1 break;
         proxy_pass https://api.shanbay.com/;
         }
    }
           

這樣的話,我們隻用處理

/api

下的url。

在配置檔案中我們通過

rewrite

将URL重寫為真正要請求的

URL

,通過

proxy_pass

代理到真實的伺服器IP或者域名。

Cookie

如果Cookie的域名部分與目前頁面的域名不比對就無法寫入。是以如果請求 www.a.com ,伺服器 proxy_pass 到 www.b.com 域名,然後 www.b.com 輸出

domian=b.com

的 Cookie,前端的頁面依然停留在 www.a.com 上,于是浏覽器就無法将 Cookie 寫入。

可在nginx反向代理中設定:

location / {
    # 頁面位址是a.com,但是要用b.com的cookie
    proxy_cookie_domain b.com a.com;  #注意别寫錯位置了 proxy_cookie_path / /;
    proxy_pass http://b.com;
}   
           

Nginx解決跨域問題通過Nginx反向代理将對真實伺服器的請求轉移到本機伺服器來避免浏覽器的"同源政策限制"。

參考文檔:

  1. Nginx反向代理配置(解決跨域問題)
  2. 這兩篇文章講解了URL重寫的規則和用法
    • nginx配置url重寫
    • nginx配置location總結及rewrite規則寫法
  3. Nginx配置檔案詳解