近期,有測(cè)試童靴跟我反饋說(shuō)是新用戶手機(jī)號(hào)注冊(cè)時(shí),后端拋出了異常,無(wú)法正常注冊(cè)。異常如下
我們可以看到,問(wèn)題出在IPUtil這個(gè)工具類中
經(jīng)過(guò)排查我也定位到問(wèn)題點(diǎn)所在,是使用第三方通過(guò)IP獲取訪問(wèn)用戶位置時(shí)出了問(wèn)題。這個(gè)問(wèn)題會(huì)在Java多商戶中出現(xiàn)
我們使用的第三方是淘寶IP地址庫(kù),這個(gè)第三方不要任何引入跟配置,簡(jiǎn)單好用。
但是,問(wèn)題來(lái)了,官方有一個(gè)通告:
下線通告: 本iP庫(kù)查詢工具將于2022年3月31日起永久關(guān)停,屆時(shí)將不提供任何的IP庫(kù)查詢功能。
也就是說(shuō)官方雖然目前暫未關(guān)停此接口,但說(shuō)不好什么時(shí)候就會(huì)不可用了。
針對(duì)與當(dāng)前的這種情況,我這里給大家提供幾種解決方案:
目標(biāo)代碼
方案一:
針對(duì)于不需要在用戶注冊(cè)時(shí)獲取用戶訪問(wèn)地址的情況,也是最簡(jiǎn)單的方法,直接注釋掉這里
方案二:
經(jīng)過(guò)我的測(cè)試,上邊淘寶第三方IP地址庫(kù)的情況,目前只是偶爾一段時(shí)間會(huì)出現(xiàn),
我們?cè)谶@里做try catch處理,讓用戶在ip這里出問(wèn)題時(shí),可以正常進(jìn)行注冊(cè)服務(wù)
方案三:
我為大家找個(gè)一種替代方案:Ip2region
Ip2region是一個(gè)基于Java的IP地址定位庫(kù),提供了快速而準(zhǔn)確的IP查詢服務(wù)。它將全球IP地址劃分為多級(jí)區(qū)域,可以根據(jù)IP地址獲取對(duì)應(yīng)的省市區(qū)信息、運(yùn)營(yíng)商等詳細(xì)信息。
官方地址: https://github.com/lionsoul2014/ip2region
使用方式:引入Maven倉(cāng)庫(kù):
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>
代碼示例:
實(shí)體類
/**
* @ClassName IpLocation
* @Description IP位置對(duì)象
* @Author HZW
* @Date 2023/6/29 11:01
* @Version 1.0
*/
public class IpLocation {
// @ApiModelProperty("ip地址")
private String ip;
// @ApiModelProperty("國(guó)家")
private String country;
// @ApiModelProperty("省")
private String province;
// @ApiModelProperty("省")
private String city;
// @ApiModelProperty("服務(wù)商")
private String isp;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getIsp() {
return isp;
}
public void setIsp(String isp) {
this.isp = isp;
}
@Override
public String toString() {
return "IpLocation{" +
"ip='" + ip + '\'' +
", country='" + country + '\'' +
", province='" + province + '\'' +
", city='" + city + '\'' +
", isp='" + isp + '\'' +
'}';
}
}
工具類
/**
* @ClassName IpUtil
* @Description TODO
* @Author HZW
* @Date 2023/6/29 11:09
* @Version 1.0
*/
public class IpUtil {
/**
* 字符常量0
*/
private static final String ZERO = "0";
/**
* 本級(jí)ip
*/
private static final String LOCALHOST = "127.0.0.1";
/**
* 獲取客戶端的IP地址
*/
public static String getIpAddress(HttpServletRequest request) {
String ipAddress = request.getHeader("X-Forwarded-For");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (LOCALHOST.equals(ipAddress)) {
// 根據(jù)網(wǎng)卡取本機(jī)配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
ipAddress = inet.getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
// 對(duì)于通過(guò)多個(gè)代理轉(zhuǎn)發(fā)的情況,取第一個(gè)非unknown的IP地址。
// 這里假設(shè)第一個(gè)IP為真實(shí)IP,后面的為代理IP。
if (ipAddress != null && ipAddress.length() > 15) {
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
return ipAddress;
}
/**
* 根據(jù)iP獲取歸屬地信息
*/
public static IpLocation getLocation(String ip) {
IpLocation location = new IpLocation();
location.setIp(ip);
try (InputStream inputStream = IpUtil.class.getResourceAsStream("/ip2region/ip2region.xdb");) {
byte[] bytes = IoUtil.readBytes(inputStream);
Searcher searcher = Searcher.newWithBuffer(bytes);
String region = searcher.search(ip);
if (StrUtil.isAllNotBlank(region)) {
// xdb返回格式 國(guó)家|區(qū)域|省份|城市|ISP,
// 只有中國(guó)的數(shù)據(jù)絕大部分精確到了城市,其他國(guó)家部分?jǐn)?shù)據(jù)只能定位到國(guó)家,后前的選項(xiàng)全部是0
String[] result = region.split("\\|");
location.setCountry(ZERO.equals(result[0]) ? StrUtil.EMPTY : result[0]);
location.setProvince(ZERO.equals(result[2]) ? StrUtil.EMPTY : result[2]);
location.setCity(ZERO.equals(result[3]) ? StrUtil.EMPTY : result[3]);
location.setIsp(ZERO.equals(result[4]) ? StrUtil.EMPTY : result[4]);
}
searcher.close();
} catch (Exception e) {
System.out.println("ip地址解析異常,error:" + e);
return location;
}
return location;
}
}
在官網(wǎng)地址,將data目錄下的ip2region.xdb文件復(fù)制到resource下。
測(cè)試:
/**
* @ClassName IpMain
* @Description TODO
* @Author HZW
* @Date 2023/6/29 11:08
* @Version 1.0
*/
public class IpMain {
public static void main(String[] args) {
// String ip = "192.168.31.164";
String ip = "219.145.115.165";
IpLocation location = IpUtil.getLocation(ip);
System.out.println(location.toString());
}
}
結(jié)果
當(dāng)然這里只是ip2region的一個(gè)Demo,在ip2region官方也有不同的使用方法,大家可以自己接入。
crmeb的ip2region接入會(huì)隨同不久后Java多商戶V1.3版本的上線一同更新,懶得動(dòng)手的同學(xué)可以到時(shí)候一起更新版本。