Apache和Nginx自定義修改Response Header中的Location值


1. 緣由

最近單位里面調整應用端口和防火牆配置,原先起在 80 端口上的服務,需要調整到 8180 端口上進行部署。為了對公網的影響度最小,公網端口依舊采用的 80 端口暴露服務,由網絡的同事在 WAF 側做了一個 80 到 8180 端口的映射。形成了一個這樣的網絡拓撲(脫敏簡化)圖。

簡單網絡拓撲圖

形成這樣的網絡拓撲之后,按理公網上訪問 http://test.snowheart.cn/ 時即可正常訪問到后台的應用服務。But,后端有一些地方用到了 sendRedirect(url) 方法,這就導致了在進行 302 網絡跳轉的時候,Response 的 Location Header 上面帶了內部的端口,即形成了下述形式的 Location Header。

HTTP/1.1 302 Moved Temporarily
Date: Mon, 11 May 2020 15:45:29 GMT
Location: http://test.snowheart.cn:8180/app/login.jsp
Content-Type: text/html;charset=utf-8
Connection: close
Transfer-Encoding: chunked

瀏覽器在進行跳轉時,使用了前面一個請求的 Location,在公網上請求了 8180 端口,因為防火牆的緣故,無法訪問,報錯。

2. 問題分析

2.1. 瀏覽器的重定向跳轉機制

在 HTTP 的 Code 響應碼中,301 表示永久重定向,302 表示臨時重定向。

  • 通過 Response 中的 HTTP Code,瀏覽器可以得知接下來要進行再次請求跳轉了。

  • 通過 Response 中的 Location,瀏覽器可以得知下一次要請求到哪里。

// DemoServlet1
// 通過設置 Header 和 Status,來進行重定向操作,其他什么都不做
response.setHeader("Location", request.getContextPath() + "/hello.html");
response.setStatus(302);

上述例子中的 Servlet URL,在瀏覽器中訪問之后,你會發現瀏覽器被重定向到了項目中的 /hello.html,其代碼在實現結果上,等價於下面的代碼:

// DemoServlet2
// 通過 sendRedirect(url) 來進行重定向操作,其他什么都不做
response.sendRedirect(request.getContextPath() + "/hello.html");

2.2. 8180 端口是在哪里添加的?

如果 WAF 上是同端口透明出去了,公網和內網前置機的 Port 一致;

鏈路上除前置機應用端口為 8180 外,其余地方端口均與此無關;

只是依賴 Apache 或 Nginx 的反向代理,是不會出現后端 Port 暴露的事情;

看來是在 WAF 做了端口轉換之后,內網前置機的 Apache 並不知道公網上的端口已經是 80 了,故在 Location 上增加了 8180 端口的信息;

亦可通過在各環節打印日志的形式,來檢測 8180 端口是哪個環節增加的...

故:我們判定,在從前置機 Apache 返回時,Location 上就已經有了前面的域名 + 端口,現在需要做的事情就是在返回之前,將 Location 上的端口給抹掉,亦或者是將 Location 上的域名 + 端口給抹掉,僅剩余從 / 開始的 URI 地址。

3. 改造實施

最先時候,想通過自己最熟悉的 Nginx 來進行 HTTP Response Header 的重寫

3.1. Nginx 配置

  • 在 Linux 虛機上安裝 Nginx 並進行測試工作(已有 Nginx 跳過)

    Windows 下的 Nginx 程序包無法進行再次編譯,沒有辦法來加載其他 Nginx 模塊;故需要使用 Linux 下的 Nginx 服務,以最大限度地與服務器保持一致。

  • 查詢 Nginx 編譯參數,安裝 ngx_headers_more 模塊,重新編譯並覆蓋 nginx 文件

# 下載最新的 ngx_headers_more 模塊包
wget https://github.com/openresty/headers-more-nginx-module/archive/v0.33.tar.gz
tar -xzvf v0.33.tar.gz

# 跳轉到 Nginx 目錄
# 查看 Nginx 編譯參數
./nginx -V

# 跳轉至 Nginx 源碼目錄,增加 ngx_headers_more 配置並編譯
# 每個人的目錄都不相同,僅供參考
./configure --prefix=/usr/local/nginx/server --with-http_stub_status_module --with-http_ssl_module --with-pcre=/tool/pcre-8.44 --with-http_realip_module --with-http_sub_module --with-http_gzip_static_module --add-module=/tool/echo-nginx-module-0.62rc1 --add-module=/tool/headers-more-nginx-module-0.33
make
# 切記,不要 make install,否則會覆蓋掉之前Nginx的配置文件這些。
cp /usr/local/nginx/server/sbin/nginx /usr/local/nginx/server/sbin/nginx.origin
cp objs/nginx /usr/local/nginx/server/sbin/nginx
  • 修改配置文件 nginx.conf
map $upstream_http_Location $location{
  # 這種方案是把所有到 8180 端口的重定向都改成 80 端口,有一定的風險,容易誤傷
  # ~http://(?<domains>.*):8180/(?<param>.*) http://$domains/$param;

  # 這種方案是針對特定域名 8180 端口的重定向,范圍可控,寫法冗長
  ~http://test.snowheart.cn:8180/(?<param>.*) http://test.snowheart.cn/$param;

  # 默認情況,保持原狀
  default $upstream_http_Location;
}

server {
  location /app {
    proxy_pass       http://192.168.36.72:7001/app;
    proxy_set_header Host               $host:$server_port;
    proxy_set_header X-Real-IP          $remote_addr;
    proxy_set_header REMOTE-HOST        $remote_addr;
    proxy_redirect   off;
    proxy_set_header X-Forwarded-Proto  $scheme;
    proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
    more_set_headers -s '301 302' 'Location $location';
  }
}
  • 重啟應用,進行測試
$ curl http://test.snowheart.cn/app/RedirectServlet --dump -

HTTP/1.1 302 
Server: nginx/1.14.2
Date: Mon, 11 May 2020 16:41:23 GMT
Content-Length: 0
Location: http://test.snowheart.cn/app/hello.html
Connection: keep-alive

大坑

Nginx 需要安裝新的 module,這又涉及到重新編譯和 root 安裝等操作,在企業里面,做這些是需要一定的流程和規范性評估的,故經過考慮后,放棄此方案。上述筆記僅作為 POC 階段使用。

Nginx 方案受阻后,使用當前測試環境在用的 Apache 來實現 Location 的重寫

3.2. Apache 配置

  • 配置文件
# httpd.conf
# 打開 Rewrite 功能
RewriteEngine On

# 對 Location 進行重寫
Header edit Location "(^http[s]?://test.snowheart.cn:8180/)" "/"
Header edit Location "(^http[s]?://123.234.123.234:8180/)" "/"
  • 測試應用
$ curl http://test.snowheart.cn/app/RedirectServlet --dump -

HTTP/1.1 302 
Server: nginx/1.14.2
Date: Mon, 11 May 2020 16:41:23 GMT
Content-Length: 0
Location: http://test.snowheart.cn/app/hello.html
Connection: keep-alive

簡單的三行配置,即達成了我們的目的。

4. 感悟

  • 從問題的實際情況出發,而不是從我們自以為的解決方案出發。我們的目的在於更快更好地解決當下的問題,而不是都一大圈子回來,做很多無謂的工作。
  • 大道至簡,往往最簡單的配置,最簡單的代碼,就可以實現我們想要的復雜的功能點。莫要鑽進牛角尖自以為是出不來。
  • 工作才是最好的老師,學中用,用中學,方能不斷進步。
  • 解決問題也不一定需要親力親為,在企業內部,有專門的中間件團隊和流程規范來保證着專業的人,做專業的事,做正確的事。多交流遠比自己琢磨要好得多。

5. 參考鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM