Nginx暴露出请求的真实IP的问题
作者:vnjohn
前言
在工作中,经常会用用户实际请求的 IP 地址,当需要记录到日志信息时或者在请求其他的第三方接口时需要用到实际的用户 IP 地址传入,在本节中会提供服务端在实际获取 IP 地址的源码以及通过 Nginx 代理后 IP 地址隐藏的问题
用途/场景
获取经纬度
无论是小程序还是 App 端,当用户未开启地理位置授权时,在前端页面是无法拿到当前用户所在的经纬度信息的,这时候还有另外一种途径来获取经纬度,那就是通过 IP 地址来解析用户真实请求时所在的经纬度
在一般情况下,都是由前端来拿到这块请求的 IP,可能由于某些 API 或版本的限制,导致前端在所获取的 IP 地址不准确,此时,不得不让后端的大佬 CASE 出手了
支付接口
微信 > 小程序支付文档中,终端IP:spbill_create_ip 参数是必填项,虽然在微信那一侧不会去校验这块的 IP 地址是否准确,但真实的 IP 地址有利用我们去排查线上问题,比如 IP 地址都是 127.0.0.1 这样的,你永远都分辨不出到底请求是从那一块服务进来的
白名单配置
当请求第三方接口 API 时,若我们所请求的 IP 地址未配置进白名单时,我们是无法从第三方获取到我们想要得到的信息的,这时候有了前置或后置日志打印我们请求时的 IP,能够更快的帮助我们解决问题
比较友善的第三方 API,在我们请求接口出现白名单的问题,会将我们的 IP 进行返回提示 “该 IP:Xxx,未配置白名单,请联系管理员!”,这时直接拿到返回的错误信息内容进行配置即可,无须任何的日志输入
但是有的第三方 API 就比较鸡肋,只是返回一段 "请求 IP 未配置白名单,请联系管理员!”,这时候如果不+日志打印,那中间去找部署服务的 IP 花费的时间就比这日志打印的时间多了个去
实现源码
请求工具类
/** * @author vnjohn * @since 2023/10/30 */ public class IPv4Util { private static final String HEADER_FORWARDED_FOR = "x-forwarded-for"; private static final String HEADER_PROXY_CLIENT_IP = "Proxy-Client-IP"; private static final String HEADER_WL_PROXY_CLIENT_IP = "WL-Proxy-Client-IP"; private static final String HEADER_HTTP_CLIENT_IP = "http_client_ip"; private static final String HEADER_HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR"; private static final String UNKNOWN = "unknown"; private static final String CHAR_COLON = ":"; private static final String CHAR_COMMA = ","; public static String getClientIp(HttpServletRequest request) { String ip = request.getHeader(HEADER_FORWARDED_FOR); boolean ipIsEmpty = ip == null || ip.length() == 0; if (ipIsEmpty || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader(HEADER_PROXY_CLIENT_IP); } if (ipIsEmpty || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader(HEADER_WL_PROXY_CLIENT_IP); } if (ipIsEmpty || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } if (ipIsEmpty || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader(HEADER_HTTP_CLIENT_IP); } if (ipIsEmpty || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader(HEADER_HTTP_X_FORWARDED_FOR); } // 如果是多级代理,那么取第一个ip为客户ip if (ip != null && ip.contains(CHAR_COMMA)) { ip = ip.substring(ip.lastIndexOf(CHAR_COMMA) + 1, ip.length()).trim(); } //判断IP是否存在带有端口号的情况、应该要去掉端口号 if (ip != null && ip.contains(CHAR_COLON)) { ip = ip.substring(0, ip.indexOf(CHAR_COLON)); } return ip; } /** * 获取请求的ip */ public static String getRequestIp() { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); // 从获取 RequestAttributes 中获取 HttpServletRequest 信息 HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } public static void main(String[] args) { System.out.println(getRequestIp()); } }
在其他地方,需要获取 IP 时,只需要按如下传入参数:
HttpServletRequest request
如下调用即可:
IPv4Util.getClientIp(request);
Nginx 代理配置
在 server 标签模块中指定的请求前缀配置一个 location 或直接在 server 标签内进行配置,如下:
location /app { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://localhost:8084; }
只有当设置了 proxy_set_header X-Real-IP 代理参数以后,获取服务请求的 IP 才是准确的
小结
注意:只有在 Nginx 代理配置以下内容以后,通过请求工具类才能获取到真实的 IP 地址!!!
总结
该篇博文主要介绍的是如何在服务端中获取用户请求的真实 IP 地址,通过几种用途/场景介绍其所在的必要性:获取经纬度、支付接口、白名单配置,当然,为了让你能够快速的运用起来,提供了请求工具类的源码以及告知你如何在 Nginx 进行代理配置,希望这篇短文能够帮助到您,解决您实际工作中的一些问题
到此这篇关于Nginx暴露出请求的真实IP的问题解决的文章就介绍到这了,更多相关Nginx暴露请求的真实IP内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!