无Nginx代理场景 业务层通过获取请求头参数即可拿到客户端IP request.getRemoteAddr(); 一级Nginx代理 使用代理后直接读取请求头参数会读取到代理服务器的IP地址，而非真实客户端IP 解决方法是添加Nginx请求头参数提前保存客户端IP nginx.conf配置加入内容 location / { ... # IP地址转发 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-Port $remote_port; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } 业务层读取nginx配置的请求头参数即可 public static String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("X-Real-IP"); if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); log.info("【Proxy-Client-IP】 {}", ip); } if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); log.info("【WL-Proxy-Client-IP】{}", ip); } if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Forwarded-For"); log.info("【X-Forwarded-For】{}", ip); } if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); log.info("【unknown】{}", ip); } return ip; } 多级Nginx代理 若存在多级Nginx代理，则需要在第一级代理时获取客户端IP，在后续代理逐层传递 第一级代理配置同上，第N级代理配置nginx.conf如下 location /{ # IP地址转发 proxy_set_header X-Real-IP $X-Real-IP; proxy_set_header X-Real-Port $X-Real-Port; proxy_set_header X-Forwarded-For $X-Forwarded-For; } 业务层保持不变即可读取到传递的IP地址 阿里云CDN转发 在已经存在nginx代理的场景下，加入CDN后源客户端IP在CDN处被转发，故第一级nginx代理使用$remote_addr参数读取到的是CDN服务的地址，而根据一般约定，CDN转发会将IP地址存放在X-Forwarded-For参数下 修改顶级Nginx配置以支持优先获取CDN代理地址，也可以通过修改业务层读取优先级实现 location / { ... # IP地址转发 # proxy_set_header X-Real-IP $remote_addr; # proxy_set_header X-Real-Port $remote_port; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } 但经过对阿里云CDN测试，发现X-Forwarded-For下不仅包含真实客户端IP也包含CDN服务IP，故业务层需要做一定的处理进行区分 如图，第一个为真实客户端IP，第二个为CDN代理IP public static String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("X-Real-IP"); if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); log.info("【Proxy-Client-IP】 {}", ip); } if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); log.info("【WL-Proxy-Client-IP】{}", ip); } if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) { ip = request.getHeader("X-Forwarded-For"); if (ip.contains(",")) { // 通过阿里云CDN转发后可能读取到2个IP地址 String[] cdnMutilIp = ip.split(","); ip = cdnMutilIp[0]; } log.info("【X-Forwarded-For】{}", ip); } if (ip == null || ip.length() == 0 || " unknown ".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); log.info("【unknown】{}", ip); } return ip; } }
问题产生 最近有一个新的业务系统上线运行，源站使用8085端口，通过Nginx反向代理到80和443端口，为了保证全国的延迟在50ms以内，故启用CDN加速，仅对js和css等静态文件加速，其余文件自动回源，然而一旦开启了静态文件CDN后，网页便无法渲染（Vue），通过控制台查看，报重定向（301）过多的错误 一旦关闭CDN对js和css的加速后再清除浏览器缓存就立刻可以访问 寻找解决办法 经过网上查阅资料无果后向阿里云提交工单咨询 过程十分曲折，在阿里云工程师的指引下反复修改CDN各种配置，从上午一直修改到晚上10点，更换了3个工程师（从工号判断），其中还包括阿里工程师直接打电话询问问题情况（好评） 测试途径包括但不限于：跟随回源协议、抓包等 目前折衷方案 根据阿里的建议，现在只能暂停对静态文件的加速，全部回源到源站 造成的影响 2018年4月9日全天整个业务系统通过域名访问处于崩溃状态，对所有需要访问的客户重定向到IP+端口号的地址，并且该地址还一直被微信拦截（微信拦截非80端口的网址）