细心的小伙伴可能会发现,最近蘑菇新上线了IP属地的功能,小伙伴在发表动态、发表评论以及聊天的时候,都会显示自己的IP属地信息 动态显示IP属地 在蘑菇群聊中,也可以展示IP属地,下面是小伙伴们在交流群中显示的 下面,我就来讲讲,Java中是如何获取IP属地的,主要分为以下几步通过HttpServletRequest对象,获取用户的IP地址通过IP地址,获取对应的省份、城市 首先需要写一个IP获取的工具类,因为每一次用户的Request请求,都会携带上请求的IP地址放到请求头中。publicclassIpUtil{publicstaticStringgetIpAddr(ServerHttpRequestrequest){HttpHeadersheadersrequest。getHeaders();StringipAddressheaders。getFirst(XForwardedFor);if(ipAddressnullipAddress。length()0unknown。equalsIgnoreCase(ipAddress)){ipAddressheaders。getFirst(ProxyClientIP);}if(ipAddressnullipAddress。length()0unknown。equalsIgnoreCase(ipAddress)){ipAddressheaders。getFirst(WLProxyClientIP);}if(ipAddressnullipAddress。length()0unknown。equalsIgnoreCase(ipAddress)){ipAddressrequest。getRemoteAddress()。getAddress()。getHostAddress();if(ipAddress。equals(127。0。0。1)ipAddress。equals(0:0:0:0:0:0:0:1)){根据网卡取本机配置的IPtry{InetAddressinetInetAddress。getLocalHost();ipAddressinet。getHostAddress();}catch(UnknownHostExceptione){log。error(根据网卡获取本机配置的IP异常,e);}}}对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照,分割if(ipAddress!nullipAddress。indexOf(,)0){ipAddressipAddress。split(,)〔0〕;}returnipAddress;}} 这里有三个名词,分别是XForwardedFor:一个HTTP扩展头部,主要是为了让Web服务器获取访问用户的真实IP地址。每个IP地址,每个值通过逗号空格分开,最左边是最原始客户端的IP地址,中间如果有多层代理,每层代理会将连接它的客户端IP追加在XForwardedFor右边。XRealIP:一般只记录真实发出请求的客户端IPProxyClientIP:这个一般是经过Apachehttp服务器的请求才会有,用Apachehttp做代理时一般会加上ProxyClientIP请求头WLProxyClientIP:也是通过Apachehttp服务器,在weblogic插件加上的头。 在我们获取到用户的IP地址后,那么就可以获取对应的ip信息了 蘑菇最开始使用的是淘宝IP库 地址:https:ip。taobao。com 接入方式也比较简单,就是通过封装一个http请求,传入用户的ip作为参数,就可以返回ip对应的国家,省,城市信息 原来的请求方式如下获取IP地址来源paramcontent请求的参数格式为:namexxxpwdxxxparamencodingString服务器端请求编码。如GBK,UTF8等returnthrowsUnsupportedEncodingExceptionpublicstaticStringgetAddresses(Stringcontent,StringencodingString){Stringipcontent。substring(3);if(!Util。isIpAddress(ip)){log。info(IP地址为空);returnnull;}淘宝IP宕机,目前使用Ip2region:https:github。comlionsoul2014ip2regionStringcityInfogetCityInfo(ip);log。info(返回的IP信息:{},cityInfo);TODO淘宝接口目前已经宕机,因此暂时注释下面代码try{这里调用pconline的接口StringurlStrhttp:ip。taobao。comservicegetIpInfo。php;从http:whois。pconline。com。cn取得IP所在的省市区信息StringreturnStrgetResult(urlStr,content,encodingString);if(returnStr!null){处理返回的省市区信息log。info(调用IP解析接口返回的内容:returnStr);String〔〕tempreturnStr。split(,);无效IP,局域网测试if(temp。length3){return0;}国家Stringcountry;区域Stringarea;省Stringregion;市Stringcity;县Stringcounty;运营商Stringisp;MapString,ObjectmapJsonUtils。jsonToMap(returnStr);if(map。get(code)!null){MapString,Stringdata(MapString,String)map。get(data);countrydata。get(country);areadata。get(area);regiondata。get(region);citydata。get(city);countydata。get(area);ispdata。get(isp);}log。info(获取IP地址对应的地址countryarearegioncitycountyisp);StringBufferresultnewStringBuffer();result。append(country);result。append();result。append(region);result。append();result。append(city);result。append();result。append(isp);returnresult。toString();}}catch(Exceptione){log。error(e。getMessage());returnnull;}returnnull;} 但是,之前接入淘宝IP库的时候,也经常会遇到服务不可用的情况,并且由于限制了QPS为1,所以如果访问量大的话,就没办法获取了。 而到现在的话倒好了,这个接口也不对外提供服务了,直接下线了,不让调用了。 后面,陌溪在Github冲浪的时候,发现了Ip2region项目。 一个准确率99。9的离线IP地址定位库,0。0x毫秒级查询,ip2region。db数据库只有数MB,提供了java,php,c,python,nodejs,golang,c等查询绑定和Binary,B树,内存三种查询算法。 数据聚合了一些知名ip到地名查询提供商的数据,这些是他们官方的的准确率,经测试着实比经典的纯真IP定位准确一些。ip2region的数据聚合自以下服务商的开放API或者数据。80,淘宝IP地址库,http:ip。taobao。com10,GeoIP,https:geoip。com2,纯真IP库,http:www。cz88。net 备注:如果上述开放API或者数据都不给开放数据时ip2region将停止数据的更新服务。 每条ip数据段都固定了格式:城市Id国家区域省份城市ISP 只有中国的数据精确到了城市,其他国家有部分数据只能定位到国家,后前的选项全部是0,已经包含了全部你能查到的大大小小的国家 生成的数据库文件ip2region。db只有几MB,最小的版本只有1。5MB,随着数据的详细度增加数据库的大小也慢慢增大,目前还没超过8MB。内置的三种查询算法 全部的查询客户端单次查询都在0。x毫秒级别,内置了三种查询算法memory算法:整个数据库全部载入内存,单次查询都在0。1x毫秒内,C语言的客户端单次查询在0。00x毫秒级别。binary算法:基于二分查找,基于ip2region。db文件,不需要载入内存,单次查询在0。x毫秒级别。btree算法:基于btree算法,基于ip2region。db文件,不需要载入内存,单词查询在0。x毫秒级别,比binary算法更快。ip2region安装 下面,就让我们给项目引入ip2region,进行ip信息转换吧 首先引入maven依赖dependencygroupIdorg。lionsoulgroupIdip2regionartifactIdversion1。7。2versiondependency 然后编写一个工具类IpUtils,首先需要加载ip2region。db文件static{dbPathcreateFtlFileByFtlArray()ip2region。db;try{confignewDbConfig();}catch(DbMakerConfigExceptione){e。printStackTrace();}try{searchernewDbSearcher(config,dbPath);}catch(FileNotFoundExceptione){e。printStackTrace();}} 在加载的时候,需要下载仓库中的ip2region。db文件,然后放到resource目录下 然后,通过内置的三种算法,分别转换用户ip地址publicstaticStringgetCityInfo(Stringip){if(StringUtils。isEmpty(dbPath)){log。error(Error:Invalidip2region。dbfile);returnnull;}if(confignullsearchernull){log。error(Error:DbSearcherorDbConfigisnull);returnnull;}查询算法Btree,B树搜索(更快)intalgorithmDbSearcher。BTREEALGORITHM;Binary,使用二分搜索DbSearcher。BINARYALGORITHMMemory,加载内存(最快)DbSearcher。MEMORYALGORITYMtry{使用静态代码块,减少文件读取操作DbConfigconfignewDbConfig();DbSearchersearchernewDbSearcher(config,dbPath);definethemethodMethodmethodnull;switch(algorithm){caseDbSearcher。BTREEALGORITHM:methodsearcher。getClass()。getMethod(btreeSearch,String。class);break;caseDbSearcher。BINARYALGORITHM:methodsearcher。getClass()。getMethod(binarySearch,String。class);break;caseDbSearcher。MEMORYALGORITYM:methodsearcher。getClass()。getMethod(memorySearch,String。class);break;default:}DataBlockdataBlocknull;if(Util。isIpAddress(ip)false){System。out。println(Error:Invalidipaddress);}dataBlock(DataBlock)method。invoke(searcher,ip);StringipInfodataBlock。getRegion();if(!StringUtils。isEmpty(ipInfo)){ipInfoipInfo。replace(0,);ipInfoipInfo。replace(0,);}returnipInfo;}catch(Exceptione){e。printStackTrace();}returnnull;} 下面,我们编写main函数进行测试,发现可以正常的解析出ip信息 由于ip属地在国内的话,只会展示省份,而国外的话,只会展示国家。所以我们还需要对这个方法进行一下封装,得到获取IP属地的信息。获取IP属地paramipreturnpublicstaticStringgetIpPossession(Stringip){StringcityInfogetCityInfo(ip);if(!StringUtils。isEmpty(cityInfo)){cityInfocityInfo。replace(,);String〔〕cityListcityInfo。split();if(cityList。length0){国内的显示到具体的省if(中国。equals(cityList〔0〕)){if(cityList。length1){returncityList〔1〕;}}国外显示到国家returncityList〔0〕;}}return未知;} 下面,我们在找一个国外的IP测试一下效果。可以看到已经能够正常的显示IP属地信息了 到这里如果获取用户的IP属地已经完成啦,如果想要了解关于更多ip2region的功能,欢迎访问其Github地址进行学习。项目地址 https:github。comlionsoul2014ip2region 来源:https:mp。weixin。qq。comsGcgvqlvtklUrT2mB7fIrQ