近期,有測試童靴跟我反饋說是新用戶手機(jī)號注冊時(shí),后端拋出了異常,無法正常注冊。異常如下
我們可以看到,問題出在IPUtil這個(gè)工具類中
經(jīng)過排查我也定位到問題點(diǎn)所在,是使用第三方通過IP獲取訪問用戶位置時(shí)出了問題。這個(gè)問題會(huì)在Java多商戶中出現(xiàn)
我們使用的第三方是淘寶IP地址庫,這個(gè)第三方不要任何引入跟配置,簡單好用。
但是,問題來了,官方有一個(gè)通告:
下線通告: 本iP庫查詢工具將于2022年3月31日起永久關(guān)停,屆時(shí)將不提供任何的IP庫查詢功能。
也就是說官方雖然目前暫未關(guān)停此接口,但說不好什么時(shí)候就會(huì)不可用了。
針對與當(dāng)前的這種情況,我這里給大家提供幾種解決方案:
目標(biāo)代碼
方案一:
針對于不需要在用戶注冊時(shí)獲取用戶訪問地址的情況,也是最簡單的方法,直接注釋掉這里
方案二:
經(jīng)過我的測試,上邊淘寶第三方IP地址庫的情況,目前只是偶爾一段時(shí)間會(huì)出現(xiàn),
我們在這里做try catch處理,讓用戶在ip這里出問題時(shí),可以正常進(jìn)行注冊服務(wù)
方案三:
我為大家找個(gè)一種替代方案:Ip2region
Ip2region是一個(gè)基于Java的IP地址定位庫,提供了快速而準(zhǔn)確的IP查詢服務(wù)。它將全球IP地址劃分為多級區(qū)域,可以根據(jù)IP地址獲取對應(yīng)的省市區(qū)信息、運(yùn)營商等詳細(xì)信息。
官方地址: https://github.com/lionsoul2014/ip2region
使用方式:引入Maven倉庫:
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>
代碼示例:
實(shí)體類
/**
* @ClassName IpLocation
* @Description IP位置對象
* @Author HZW
* @Date 2023/6/29 11:01
* @Version 1.0
*/
public class IpLocation {
// @ApiModelProperty("ip地址")
private String ip;
// @ApiModelProperty("國家")
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";
/**
* 本級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();
}
}
}
// 對于通過多個(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返回格式 國家|區(qū)域|省份|城市|ISP,
// 只有中國的數(shù)據(jù)絕大部分精確到了城市,其他國家部分?jǐn)?shù)據(jù)只能定位到國家,后前的選項(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下。
測試:
/**
* @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í)候一起更新版本。