前言
通常工作中反向代理需要攜帶上前端請求的cookie,在nginx反向代理中配置proxy_set_header Cookie $http_cookie;
,這樣后端就可以接收到瀏覽器上送的cookie了。但是這種方案只是適合前端請求的cookieName就是后端需要的。但是如果cookieName不同,需要進行轉換呢?
背景
最近工作中遇到個問題,系統A和系統B需要進行整合為一個門戶系統(要進行統一認證),A系統沒有使用公司網關,用戶認證是自己開發的web層(實現spring-security filter進行攔截到sso認證),系統B只提供dubbo服務,B的web層是網關,由網關進行用戶認證和鑒權,然后網關調用系統B的dubbo服務。系統A的web層等同系統B的網關。無論是網關還是自己系統的web層,認證都是使用的oauth2流程到公司的sso系統進行統一認證。公司sso是session存放在redis,由sessionId作為redis key,而cookie值就是sessionid的base64編碼,那么可以通過cookie就判斷用戶是否進行了登錄(根據cookie查找到)。
系統A session使用的cookie名稱是mobileSessionId,系統B session使用的cookie名稱是SESSIONID,那么在系統A經過登錄產生的cookie在訪問系統B的時候,可否通過nginx轉換下cookie名稱從而在A系統也可以直接使用呢?經過google,發現nginx的headers-more-nginx-module
模塊可以實現。
headers-more-nginx-module模塊在nginx並不自帶,需要進行安裝,下面說下步驟
安裝nginx和headers-more-nginx-module模塊
yum install nginx安裝的nginx是無法進行編譯的,比較麻煩,headers-more-nginx-module模塊安裝需要進行編譯,因此安裝nginx使用源碼安裝
源碼方式安裝nginx
root用戶登錄,當前用戶路徑是/root
下載nginx源碼包wget http://nginx.org/download/nginx-1.17.8.tar.gz
解壓tar -zxvf nginx-1.17.8.tar.gz
進入上步解壓目錄cd /root/nginx-1.17.8
編譯安裝nginx
./configure --prefix=/etc/nginx --with-debug #nginx安裝到/etc/nginx目錄,安裝debug,方便調試
make
make install
ln -s /etc/nginx/sbin/nginx /usr/sbin/nginx #軟連,方便使用nginx命令
# --with-debug是安裝nginx的debug模式,出了問題方便查看 http://nginx.org/en/docs/debugging_log.html
安裝headers-more-nginx-module模塊
從https://github.com/openresty/headers-more-nginx-module下載headers-more-nginx-module模塊
./configure --prefix=/etc/nginx \ #安裝模塊到nginx的安裝目錄
--add-module=/root/headers-more-nginx-module-0.33 \ #headers-more-nginx-module模塊下載解壓目錄
--with-debug #安裝nginx debug
make
make install
安裝參考https://github.com/openresty/headers-more-nginx-module
nginx配置修改cookie名稱實戰
啟動nginx:nginx
通過nginx -v只能查看nginx版本,nginx -V可以查看nginx更多版本信息(包含編譯)
查看nginx使用的配置文件nginx -t如下
注意這里有個坑是yum install nginx安裝的nginx默認會使用conf.d目錄下以.conf結尾的文件作為配置,但是源碼編譯安裝nginx方式不會,原因是yum安裝的nginx的nginx.conf內有
include /etc/nginx/conf.d/*.conf;,而源碼方式安裝的nginx配置內沒有。由於之前都是使用的yum方式安裝nginx,自定義配置都是放conf.d目錄生效,這次使用源碼方式安裝,放心無論如何不生效,排查后發現這個區別。
創建/etc/nginx/conf.d
目錄,修改/etc/nginx/conf/nginx.conf
在http模塊配置內增加include /etc/nginx/conf.d/*.conf;
,這樣自己放/etc/nginx/conf.d
目錄下的配置就可以生效。創建/etc/nginx/conf.d/switchcookie.conf
,內容如下:
log_format main
'[$time_local] - $remote_addr:$remote_port - $remote_user - $upstream_addr $upstream_status $upstream_response_time - '
'"$request" $status $bytes_sent $request_time '
'"$http_referer" - "$http_user_agent" '
'"$http_cookie"'; #nginx日志打印cookie
server {
access_log logs/access.log main;
error_log logs/error.log error;
listen 80;
server_name zzz.yhd.com;
client_max_body_size 50m;
location /api {
add_header Access-Control-Allow-Origin 'zzz.yhd.com';
add_header Access-Control-Allow-Credentials 'true';
add_header Access-Control-Allow-Headers 'Origin, X-Requested-With, Content-Type, Accept';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
proxy_set_header Host $host;
proxy_set_header x-real-ip $remote_addr;
proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
#headers-more-nginx-module模塊--start
if ($cookie_mobileSessionId) { #前端上送的cookie中有cookie名稱為mobileSessionId則判斷為true
more_set_input_headers "Cookie: SESSION=$cookie_mobileSessionId; $http_cookie";
#more_set_input_headers "Cookie: SESSION=$cookie_mobileSessionId";
}
#headers-more-nginx-module模塊--end
proxy_pass http://ares.zzz.com/api;
#proxy_redirect off;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404;
}
}
這個配置就是瀏覽器訪問http://zzz.yhd.com/api
(Cookie:mobileSessionId=ZDE4NzI3YTItZTMxNy00YWRlLTgwZGItNjUwNjYwMjkxZTgx),從而反向代理到http://ares.zzz.com/api
(Cookie:SESSIONID=ZDE4NzI3YTItZTMxNy00YWRlLTgwZGItNjUwNjYwMjkxZTgx),這樣就把cookie名稱通過nginx配置轉換了。
配置說明如下:
$cookie_mobileSessionId
是說明前端上送的cookie名稱是mobileSessionId,如果cookie名稱是SESSION,那么就用$cookie_SESSIONID
if ($cookie_mobileSessionId)
說明前端上送的cookie中有名稱mobileSessionId,如果上送了則執行more_set_input_headers邏輯
more_set_input_headers "Cookie: SESSION=$cookie_mobileSessionId; $http_cookie";
反向代理到ares.zzz.com增加cookie名稱為SESSIONID值為mobileSessionId的值,同時把前端請求的cookie也轉發到ares.zzz.com。
more_set_input_headers "Cookie: SESSION=$cookie_mobileSessionId"
反向代理到ares.zzz.com增加cookie名稱為SESSIONID值為mobileSessionId的值,不攜帶前端請求上送的cookie。
$cookie_NAME #客戶端請求Header頭中的cookie變量,前綴"$cookie_"加上cookie名稱的變量,該變量的值即為cookie名稱的值
$cookie_COOKIE 客戶端請求中COOKIE頭域的值
$http_cookie 客戶端cookie信息
實戰截圖如下
通過nginx這樣配置,請求測試正常
域名zzz.yhd.com前端所在服務器的nginx日志顯示如下
域名ares.zzz.com前端所在服務器nginx日志顯示如下
流程圖
總結
通過這種方式,在整合A和B兩個系統時候,無需改動B系統(走的網關),這樣就統一對系統A和B進行了認證。否則系統B要開發下自己的web層,這樣減少了系統B開發web層的工時,提升了效率。
代理到容器域名問題
在做修改cookieName的時候,域名反向代理到虛擬機環境的域名是正常,但是代理到容器域名就不行,經過排除和容器組同時溝通得知,容器的域名是通過ingress進行反向代理的,接收到的域名必須是容器域名,否則不識別。因此我的配置內的 proxy_set_header Host $host
; 在反向代理到容器域名時候,ingress接收到的是http://zzz.yhd.com
,因此在proxy_pass到容器域名時候,必須要改為proxy_set_header Host "http://ares.zzz.com"
; 這樣容器ingress接收到的域名才是配置在容器內的域名,才可以正常反向代理到內部容器。
后續又遇到同事開發釘釘小程序,外網請求內部服務反向代理到內部服務容器域名,也是此問題,改為proxy_set_header Host "http://ares.zzz.com"
;解決。
nginx常用命令總結
nginx啟動 nginx
nginx停止 nginx -s quit (優雅停止) nginx -s stop(硬停止)
重新加載 nginx -s reload 修改了配置需要執行此命令
顯示nginx版本 nginx -v
顯示nginx版本(詳細) nginx -V
顯示nginx使用的配置 nginx -t
參考