持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的5天,点击查看活动详情
近期很多平台(微博,抖音,微信等)都开放了 IP 属地的显示,这个功能挺有趣的,同时我也琢磨了很久,想在自己的项目中使用,现在就写下这篇文章谈谈我的思路;
实现
如何获取IP地址
从 HttpServletRequest 中可以获取到客户端在请求头中携带的IP地址
头部名词的解释
- X-Forwarded-For:Web 服务器获取访问用户的真实 IP 地址,若存在代理,最左边是最原始客户端的 IP 地址;
- Proxy-Client-IP:经过 Apache http 服务器的请求才会有的请求头
- WL-Proxy-Client-IP:也是通过 Apache http 服务器,在 weblogic 插件加上的头。
// 获取IP
public static String getIP(HttpServletRequest request) {
String ipStr = request.getHeader("x-forwarded-for");
if (StringUtils.isBlank(ipStr) || "unknown".equalsIgnoreCase(ipStr)) {
ipStr = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isBlank(ipStr) || "unknown".equalsIgnoreCase(ipStr)) {
ipStr = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isBlank(ipStr) || "unknown".equalsIgnoreCase(ipStr)) {
ipStr = request.getRemoteAddr();
}
// 多个路由时,取第一个非unknown的ip
final String[] arr = ipStr.split(",");
for (final String str : arr) {
if (!"unknown".equalsIgnoreCase(str)) {
ipStr = str;
break;
}
}
//目的是将localhost访问对应的ip 0:0:0:0:0:0:0:1 转成 127.0.0.1。
return ipStr.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ipStr;
}
如何获取位置
- 使用百度开放平台的IP定位Api;
public static String getLocationByIP(HttpServletRequest request) {
// 根据请求获取IP
String ip = getIP(request);
String location = null;
// 地址模板 https://api.map.baidu.com/location/ip?ak=AK&ip=ip&coor=xxx
String urlString = "https://api.map.baidu.com/location/ip?ak=" + AK + "&ip=" + ip + "&coor=bd09ll&oe=utf8";
try {
// 创建请求链接
URL url = new URL(urlString);
// 开始请求
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// 设置请求
urlConnection.setRequestMethod("GET");
// 设置请求超时时间
urlConnection.setConnectTimeout(15000);
int responseCode = urlConnection.getResponseCode();
// 判断请求是否成功
if (responseCode >= 200 && responseCode < 300) {
InputStream inputStream = urlConnection.getInputStream();
// 获取响应的信息
byte[] bytes = new byte[1024];
int len = 0;
StringBuilder stringBuilder = new StringBuilder();
while ((len = inputStream.read(bytes)) != -1) {
String s = new String(bytes, 0, len);
stringBuilder.append(s);
}
// 将json转换为IPEntity对象
String string = stringBuilder.toString();
Gson gson = new Gson();
IPEntity ipEntity = gson.fromJson(string, IPEntity.class);
// 判断获取的信息是否有误
String status = ipEntity.getStatus();
if (Integer.parseInt(status) == 0) {
// 获取省份
location = ipEntity.getContent().getAddress_detail().getProvince();
log.info("IP地址为:" + location);
} else {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "请求的IP有误");
}
inputStream.close();
} else {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "请求百度API失败");
}
} catch (IOException e) {
e.printStackTrace();
}
return location;
}
- 使用GitHub上的 IP2region,读者自行前去查看;
方式
我在实现的时候一直纠结于以下两种方式:
- 🔥用户表添加 IP 字段,每次 登录或注册 的时候进行查询,并且更新,即将 IP 捆绑在用户表中;
优点:
- 统一管理,其他业务可以直接从该表获取用户的 IP;
- 若用户前往另一个属地,也可以准确地查找出来;
缺点:
- 当其他地方需要使用到 IP 地址时,就要进行多一次查询
- 🔥直接在评论表中添加 IP 的字段,即在相应的业务表中添加字段;
优点:
- 查询速度快,直接从表中获取到 IP 字段;
缺点:
- 不可重复使用,当有其他地方需要显示IP时,就要在其他地方继续添加IP字段;
- 后期更改不够方便,因为用户一旦改变了IP地址,之前所存储到的用户IP都是错误的,但是这种事情发生的概率较低,因为我们记录的是省份,用户跨省流动的概率较低;
效果
我是基于第一种方法来实现的,在用户表中记录用户的IP地址,其他业务中用到就直接查询即可;