问题的提出:最近单位遇到一个需求,单位a和单位b,都通过专线连接到我单位,单位b提出需要访问单位a网络中的一个网站应用,本来很简单问题,只需要我单位中一台可以访问两边网络的服务器上,架设nginx就可以解决该问题,事实上,我天真了!

(ps:本文仅针对对nginx反向代理有一定了解的朋友,如不了解请自行百度)

问题出现在这个网站应用上,他们使用了cas架构,在系统登录的url地址和应用的地址不在一起:如下

nginx反向代理cas应用实践(多地址跳转)_网络

当使用系统的地址访问时,他跳转到下面的位置,显示了登录页面:

nginx反向代理cas应用实践(多地址跳转)_网络_02


当登录系统后,又跳转回192.168.20.150这个地址上。

查了一下cas,应该是用于登录权限管理的。

通过fiddle跟踪,发现在使用http://192.168.20.164登录时,返回了如下信息

nginx反向代理cas应用实践(多地址跳转)_nginx_03

红线标出了,浏览器下次跳转的url,继续跳转,返回:

nginx反向代理cas应用实践(多地址跳转)_网络_04

继续跳,返回:

nginx反向代理cas应用实践(多地址跳转)_nginx_05

到这里,再跳转到index.aspx就进入系统了。

是不是有点晕!总结一下:

进入系统,一共进行了三次跳转,系统的登录首页在192.168.20.164上面,点击登录成功后,就跳转到192.168.20.150上面去。

可以看出192.168.20.164是一个管理登录的服务器,系统的真实服务器在192.168.20.150上面。


好了,大体情况清楚了,但问题是如何实现我们的需求呢??

1、通过网络方式,开通两边的网络,这样肯定应该是可以,但太暴力了,也不允许,不现实,pass掉。

2、在中间服务器,使用nginx反向代理,但常规的方式,显然满足不了当前的情况,如果浏览器根据响应返回的location跳转,就会访问不到了,只能看如何在响应头返回前端浏览器前,更改掉Location,让它继续指向我们自己的nginx服务器地址。

所以,我安装一个台新的centos服务器在192.168.253.155上面,安装nginx 1.9.9,因为需要改变响应头中的Location值,需额外安装ngx_headers_more模块。

          在《nginx替换响应头(重点:如何在替换时加上if判断)》这篇文章中有详细的介绍。

nginx.conf主要配置如下:

    map $upstream_http_Location $location{
        ~http://192.168.20.150/(?<param>.*) $param;
         default $upstream_http_Location;
    }

... ...

    server {
        listen       80;
        server_name  localhost;

        location /xt/ {
             set $qz http://192.168.253.155/;
             more_set_headers -s '302' 'Location:$qz$location';
             add_header Cache-Control 'no-cache';
             add_header Cache-Control 'no-store';

             sub_filter  '/smportal-cas/'   '/xt/smportal-cas/';
             sub_filter_once off;
             proxy_pass http://192.168.20.164:XXXX/;
        }


       location / {
... ...
             proxy_pass http://192.168.20.150/;
        }

... ...

最上面的map用于将登录页面返回响应头中Location为的内容映射到变量$location中去,我们可以看到,在192.168.20.164上登录成功后,首先就跳转到http://192.168.20.150/xxx/xxx.aspx?ticket=xxxxxx,如下图:

nginx反向代理cas应用实践(多地址跳转)_nginx_03

这时我们通~http://192.168.20.150/(?<param>.*) $param;取出150/后面的内容(因为每次后面的内容会不同)赋到$location中。

(注:(?<xxx> xxxx)是nginx中通过正则取值到变量的方法,此处首先赋值给了$param,然后通过map映射到$location。

至于为什么不用$upstream_http_Location直接取值,用if判断呢?前面推荐的那篇文章写的很清楚。)


接下来配置,将http://192.168.253.155/xt/的访问代理到http://192.168.20.164上面,让用户可以访问登录页面。

其中

             set $qz http://192.168.253.155/; #设置变量$qz为我们自己的地址
             more_set_headers -s '302' 'Location:$qz$location'; #将$qz和$location(保存着刚才取到需跳转url的后面路径和参数部分)拼接成指向我们自己nginx的地址,即替换原先url中http://192.168.20.150为http://192.168.253.155


最后配置http://192.168.253.155/的访问代理到http://192.168.20.150上面去,即完成了本次的任务。

(ps: sub_filter的部分,是因为在登录页面中有很js,包括登录按钮提交时的url都使用了绝对的地址,所以需要在返回页面到前端前替换掉这些url,增加我们前辍/xt/,这样访问才没有问题。

    其实,实际情况中, 有很多的限制,比如单位b访问我单位的nginx服务器时,由于中间有网络设备不好调整,限制访问端口只能用80,而不能增加其它端口,所以配置只能使用子路径来区分多代理,本来可以用多端口就不用sub_filter替换内容了。

在实验的过程中,也尝试过用\代理登录的192.168.20.164服务器,用子路径\xt代理系统服务器,最后引出了cookie的设置问题,虽最终没这样设,但发现nginx可以设置cookie的path,以保证代理时路径改变,cookie作对应的调整,特在此备注一下:

         proxy_cookie_path  /   /xt/;#将cookie的path的/映射为 /xt/

    感觉nginx真的很方便,原先为了实现反向代理、跨域,可是用编程实现的,唉。