Nginx 反向代理及 Cookie 相關問題

最近一個項目,遇到了Nginx反向代理和Cookie的問題,遇到的問題很雜,經過一週多逐步摸索,總算有個解決方案了,做個記號,主要是記錄下遇到問題的過程,以便出現問題時備查。
【背景】

  1. 客戶原有的使用Domino開發的Web應用系統,需要部分數據通過手機端展示;
  2. 原Domino系統只能通過內網訪問,沒有域名,內網的機器都需要修改hosts來解決域名問題;(至於爲什麼沒有通過內網DNS進行域名解析設置,還不太清楚,不過這個對項目本身沒有影響)

【問題及解決辦法】

問題1:手機端需要從外網訪問,沒有域名,沒有越獄或root的手機是不能修改hosts的,且Domino服務器必須通過域名訪問。

解決方案:

  1. 增加一臺服務器,安裝Nginx作爲反向代理,申請外網IP,並且通過PAT設置,通過外網訪問到這臺Nginx,Nginx負責將請求轉發至目標服務器(Domino);
  2. 修改反向代理服務器的hosts,保證這臺機器可以正常通過hosts中的域名訪問Domino;
  3. 手機端可以通過IP訪問反向代理即可,無需域名。

問題2:Domino中,鏈接中/符號引發的問題

由於Domino的技術特點,很多鏈接都是需要寫上/的,如:src="/names.nsf?xxx",有的是頁面中這樣寫的,也有的是通過302跳轉時自動跳到這個位置,這一點與Java應用不同,Java應用中更多的是相對位置,不用寫這個/。這個符號導致的問題如下:本來希望通過http://nginx_server/myapp/的方式來訪問,將請求轉發至http://domino_server/,這個需求可以通過在nginx.conf中這樣設置解決:

 

location /myapp {
    proxy_pass http://domino_server/;
}

但是由於/的作用,導致直接訪問到了http://nginx_server/,而不再通過/myapp,導致報頁面內容找不到。
解決方案:
所以還需要在Nginx中,增加對/的反向代理:

 

location / {
    proxy_pass http://domino_server/;
}

問題3:認證問題

Domino服務器中,通過寫了一些接口代碼,提供RESTful的服務,來對手機端進行提供服務。但是由於原來的環境,沒有SSO,而且不通過認證,沒法訪問到Domino裏面的接口代碼。
解決方案:
手機端通過HTTP,模擬登錄過程

問題4:“問題3”的解決方案,由於經過了反向代理,導致Domino的Response中Cookie的Domain屬性,與反向代理的域名不一致,Cookie的Domain屬性,仍然是Domino服務器的域名。手機端拿到Cookie之後,再次進行請求的話,請求是發往反向代理的,瀏覽器認爲之前拿到的Cookie不屬於反向代理,所以Request的時候,不會把Cookie帶上,導致認證不能通過。

解決方案:

 

location / {
    proxy_cookie_domain domino_server nginx_server;
}

在Nginx上這樣設置後,可以讓反向代理修改Cookie的Domain屬性。

問題5:“問題4”的解決方案,適合反向代理服務器有域名的情況,對於沒有域名的情況,雖然瀏覽器收到的Response中有正確的Cookie信息(從Chrom開發工具的Network頁籤查看,包括域名也已經經過轉換成反向代理的域名),但是瀏覽器卻沒能正常保存這個Cookie(從Chrom開發工具的Application頁籤查看Cookie)。這一點比較奇怪,Java寫的程序,通過反向代理後,不論反向代理是IP還是域名,瀏覽器端都可以正常拿到Cookie並保存,具體原因還沒搞清。不知是否Domino服務器是否有什麼特別的安全設置。

解決方案:
在Domino服務器所在的內網,增加另一臺Java服務器,經過反向代理的請求,先發給這臺Java服務器,由這臺Java服務器將Request轉發至Domino,收到Domino的Response之後,抽取Cookie,並進行設置,以保證返回給瀏覽器的Cookie能被保存。
至此,服務器部分問題解決。


補充說明:

“問題5”中,開始的解決方案,是Java服務器將拿到的Cookie通過Response的Body返回,由頁面JS將Cookie寫入。但是這種方案,當頁面位於服務端時有效(跨域一樣有效),但是對於通過Electron打包的本地頁面,JS無法將Cookie正常寫入,這一點也暫時原因不明,不知道是否是由於瀏覽器認爲本地頁面寫的Cookie與服務端跨域?對於本地頁面,還是需要服務端在Response中正常返回Cookie。

問題6:iOS版集成Cordova後,通過本地頁面訪問服務器,收到的Cookie不能正常保存,但是直接使用Cordova打包是可以的。

解決方案:
發現Cordova直接打包的應用,AppDelegate繼承自原來的CDVAppDelegate,這個類初始化時執行了:

 

    NSHTTPCookieStorage* cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];

    [cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];

    int cacheSizeMemory = 8 * 1024 * 1024; // 8MB
    int cacheSizeDisk = 32 * 1024 * 1024; // 32MB
    NSURLCache* sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"];
    [NSURLCache setSharedURLCache:sharedCache];

將這部分代碼加入自己應用的初始化部分即可。

nginx.conf 完整配置

 

location /myapp {
     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-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
     proxy_set_header Host $host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header Cookie $http_cookie;
     proxy_pass http://domino.server/;
     proxy_cookie_domain domino.server nginx.server;
     proxy_redirect off;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章