近期,有测试童靴跟我反馈说是新用户手机号注册时,后端抛出了异常,无法正常注册。异常如下
我们可以看到,问题出在IPUtil这个工具类中
经过排查我也定位到问题点所在,是使用第三方通过IP获取访问用户位置时出了问题。这个问题会在Java多商户中出现
我们使用的第三方是淘宝IP地址库,这个第三方不要任何引入跟配置,简单好用。
但是,问题来了,官方有一个通告:
下线通告: 本iP库查询工具将于2022年3月31日起永久关停,届时将不提供任何的IP库查询功能。
也就是说官方虽然目前暂未关停此接口,但说不好什么时候就会不可用了。
针对与当前的这种情况,我这里给大家提供几种解决方案:
目标代码
方案一:
针对于不需要在用户注册时获取用户访问地址的情况,也是最简单的方法,直接注释掉这里
方案二:
经过我的测试,上边淘宝第三方IP地址库的情况,目前只是偶尔一段时间会出现,
我们在这里做try catch处理,让用户在ip这里出问题时,可以正常进行注册服务
方案三:
我为大家找个一种替代方案:Ip2region
Ip2region是一个基于Java的IP地址定位库,提供了快速而准确的IP查询服务。它将全球IP地址划分为多级区域,可以根据IP地址获取对应的省市区信息、运营商等详细信息。
官方地址: https://github.com/lionsoul2014/ip2region
使用方式:引入Maven仓库:
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>
代码示例:
实体类
/**
* @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("服务商")
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)) {
// 根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
ipAddress = inet.getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
// 对于通过多个代理转发的情况,取第一个非unknown的IP地址。
// 这里假设第一个IP为真实IP,后面的为代理IP。
if (ipAddress != null && ipAddress.length() > 15) {
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
return ipAddress;
}
/**
* 根据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返回格式 国家|区域|省份|城市|ISP,
// 只有中国的数据绝大部分精确到了城市,其他国家部分数据只能定位到国家,后前的选项全部是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;
}
}
在官网地址,将data目录下的ip2region.xdb文件复制到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());
}
}
结果
当然这里只是ip2region的一个Demo,在ip2region官方也有不同的使用方法,大家可以自己接入。
crmeb的ip2region接入会随同不久后Java多商户V1.3版本的上线一同更新,懒得动手的同学可以到时候一起更新版本。
{{item.user_info.nickname ? item.user_info.nickname : item.user_name}}
作者 管理员 企业
{{itemf.name}}
{{itemc.user_info.nickname}}
{{itemc.user_name}}
回复 {{itemc.comment_user_info.nickname}}
{{itemf.name}}