为了账号安全,请及时绑定邮箱和手机立即绑定

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

标签:
Nginx

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. 参考链接

本文由博客群发一文多发等运营工具平台 OpenWrite 发布

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
0
获赞与收藏
0

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消