在开发一些小游戏的时候,我们往往比较关注的一个功能就是分享。对于分享,我们希望根据每个城市或地区有不同的分享文。如果识别区域的功能是由服务端完成的,我们就需要知道客户端的真实IP。今天我们就来看看服务端是如何获取客户端真实IP的。nginx配置首先,一个请求要分为请求头和请求体,请求头中一般存放着我们客户端的IP地址信息。如果你的服务器使用Nginx做负载均衡,你需要在你的位置配置X-Real-IP和X-Forwarded-For请求头:location^~/your-service/{proxy_set_headerX-Real-IP$remote_addr;proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;proxy_passhttp://localhost:60000/your-service/;}1、X-Real-IP在《实战nginx》里,有这么一句话:反向代理后,由于客户端在web服务器和web服务器之间增加了一个中间层,所以web服务器无法直接获取客户端的ip,需要通过$remote_addr变量获取反向代理服务器的ip地址。这句话的意思是当你使用nginx反向服务器时,在web端使用request.getRemoteAddr()(本质上是获取$remote_addr),获取到nginx的地址,封装在$remote_addr变量中就是nginx,当然不可能获取到用户的真实ip。而nginx是可??以获取到用户真实ip的,也就是说nginx在使用$remote_addr变量的时候,是获取到用户真实ip的。如果我们要在web端获取用户的真实ip,就必须在nginx中进行赋值操作,也就是我上面的配置:proxy_set_headerX-Real-IP$remote_addr;2.X-Forwarded-ForX-Forwarded-For变量,由squid开发,用于通过HTTP代理或负载均衡器识别原始IP连接到web服务器的客户端地址的非rfc标准。如果设置了X-Forwarded-For,每次通过代理转发都会有一条记录。格式为client1、proxy1、proxy2。每个地址用逗号隔开,因为是非rfc标准,所以默认不可用,需要强制添加。默认情况下,代理转发的请求,在后端看来,远程地址是代理端的ip。也就是说,默认情况下,我们无法使用request.getAttribute("X-Forwarded-For")获取用户的ip。如果我们想通过这个变量获取用户的ip,需要在nginx中添加配置:proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;意思是给X-Forwarded-For增加一个$proxy_add_x_forwarded_for,注意增加,不是覆盖,当然因为X-Forwarded-For的默认值为空,所以我们总觉得X-Forwarded-For的值为等于$proxy_add_x_forwarded_for的值。实际上,当你在不同的ip上设置两个nginx并使用这个配置时,你会发现web服务器通过request.getAttribute("X-Forwarded-For")会得到客户端ip和第一个nginx的ip。3.那么什么是$proxy_add_x_forwarded_for?$proxy_add_x_forwarded_for变量包含客户端请求头中的X-Forwarded-For和$remote_addr两部分,以逗号分隔。比如有一个web应用,它之前是通过两个nginx转发的,www.linuxidc.com表示用户通过两个nginx访问web。在第一个nginx中,使用:proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;X-Forwarded-For当前$proxy_add_x_forwarded_for变量的部分是空的,所以只有$remote_addr,而$remote_addr的值为用户的ip,所以赋值以后,X-Forwarded-For变量的值为用户的真实IP地址。在第二个nginx上,使用:proxy_set_headerX-Forwarded-For$proxy_add_x_forwarded_for;当前的$proxy_add_x_forwarded_for变量,X-Forwarded-For部分包含了用户的真实ip,$remote_addr部分的值是之前nginx的ip地址,所以这次赋值后,X-Forwarded-For的当前值变成“用户的真实ip,第一个nginx的ip”,就一目了然了。服务器获取真实IP编码:publicstaticStringgetIpAddress(HttpServletRequestrequest){StringXip=request.getHeader("X-Real-IP");StringXFor=request.getHeader("X-Forwarded-For");if(!Strings.isNullOrEmpty(XFor)&&!"unKnown".equalsIgnoreCase(XFor)){//多次反向代理后会有多个ip值,第一个ip才是真正的ipintindex=XFor.indexOf(",");if(index!=-1){returnXFor.substring(0,index);}else{returnXFor;}}XFor=Xip;if(!Strings.isNullOrEmpty(XFor)&&!"unKnown".equalsIgnoreCase(XFor)){returnXFor;}if(Strings.nullToEmpty(XFor).trim().isEmpty()||"unknown".equalsIgnoreCase(XFor)){XFor=request.getHeader("Proxy-Client-IP");}if(Strings.nullToEmpty(XFor).trim().isEmpty()||"unknown".equalsIgnoreCase(XFor)){XFor=request.getHeader("WL-Proxy-Client-IP");}if(Strings.nullToEmpty(XFor).trim().isEmpty()||"unknown".equalsIgnoreCase(XFor)){XFor=request.getHeader("HTTP_CLIENT_IP");}if(Strings.nullToEmpty(XFor).trim().isEmpty()||"unknown".equalsIgnoreCase(XFor)){XFor=request.getHeader("HTTP_X_FORWARDED_FOR");}if(Strings.nullToEmpty(XFor).trim().isEmpty()||"unknown".equalsIgnoreCase(XFor)){XFor=request.getRemoteAddr();}returnXFor;}我们看一下各个请求头的含义:X-Real-IP:nginxproxy一般会加上这个请求头X-FORWARDED-FOR:这是squid开发的字段,只有通过HTTP代理或者负载均衡服务器时才会加上。Proxy-Client-IP和WL-Proxy-Client-IP:这通常只能通过apachehttp服务器的请求获得。使用apachehttp作为代理时,一般会加上Proxy-Client-IP请求头,WL-Proxy-Client-IP是其weblogic插件添加的头。HTTPCLIENTIP有些代理服务器会加上这个请求头。在网上搜索了一下,有一种说法:这是一个普通的http头,很容易伪造,不要轻易相信用户输入。curl-H'client-ip:8.8.8.8'lidian.club/phpinfo.php|grep_SERVER可以看到_SERVER["HTTP_CLIENT_IP"]。client-ip和client-host是在NAPT还没有普及的时代,企业内网承担的HTTP透明代理,传给服务器的header也只有极少数厂商使用。它从来就不是一个标准,也从来没有成为事实上的标准。(大家最熟悉的事实标准就是x-forwarded-for)后来出现的webproxy从来没有用过这个header。TCP/IPIllustratedVol3没有讲到这个header,网上的传言也不可信。最早的可测试痕迹出现在2005年。日本的Perl/CGI作弊书(9784798010779,第270页)通过客户端IP和标头阻止了代理用户访问。简称XFF头,代表客户端,即HTTP请求者的真实IP。只有当它通过HTTP代理(例如APACHE代理)或负载平衡服务器时,才会添加此项。不是RFC中定义的标准请求头信息,关于这一项的详细介绍可以参考squid缓存代理服务器开发文档。如果有这个信息,说明你使用了代理服务器,地址就是后面的值。可以伪造。标准格式如下:X-Forwarded-For:client1,proxy1,proxy2
