Springboot利于第三方服务进行ip定位获取省份城市等信息
在web应用的开发中,咱们或许会想知道咱们的用户恳求的来历分布状况。本文基于springboot框架,调用百度地图、高德地图的敞开渠道api,在线对对恳求来历的ip进行定位、解析,获取目标ip所对应的省份、城市、经纬度等信息,方便开发者将相关数据记录在操作日志中。
一. 共用部分
- 一致呼应类
/**
* @author 摆渡人
* @description 一致呼应类
* @date 2023/7/13 23:26
*/
@Data
@Builder
public class IpAnalyzeResponse {
/**
* 状况码
*/
private String status;
/**
* 是否成功
*/
private Boolean successFlag;
/**
* 失利原因
*/
private String msg;
/**
* 省份称号
*/
private String province;
/**
* 城市称号
*/
private String city;
/**
* 经度
*/
private Double x;
/**
* 维度
*/
private Double y;
}
- 界说接口
完成该接口,并在装备类中注入接口的完成类,即可拓展ip定位的更多完成方式
/**
* @author 摆渡人
* @description ip定位接口
* @date 2023/7/13 23:25
*/
public interface IPlatFormIpAnalyzeService {
/**
* ip定位解析
* @param ip
* @return
*/
IpAnalyzeResponse ipAnalyze(String ip);
}
- http恳求工具类(用于发送http恳求)
源码来自SmartAdmin
/**
* [ HttpUtils ]
*
* @author yandanyang
* @version 1.0
* @company 1024lab.net
* @copyright (c) 2019 1024lab.netInc. All rights reserved.
* @date
* @since JDK1.8
*/
public class SmartHttpUtil {
public static String sendGet(String url, Map<String, String> params, Map<String, String> header) throws Exception {
HttpGet httpGet = null;
String body = "";
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
List<String> mapList = new ArrayList<>();
if (params != null) {
for (Entry<String, String> entry : params.entrySet()) {
mapList.add(entry.getKey() + "=" + entry.getValue());
}
}
if (CollectionUtils.isNotEmpty(mapList)) {
url = url + "?";
String paramsStr = StringUtils.join(mapList, "&");
url = url + paramsStr;
}
httpGet = new HttpGet(url);
httpGet.setHeader("Content-type", "application/json; charset=utf-8");
httpGet.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
if (header != null) {
for (Entry<String, String> entry : header.entrySet()) {
httpGet.setHeader(entry.getKey(), entry.getValue());
}
}
HttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
throw new RuntimeException("恳求失利");
} else {
body = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
throw e;
} finally {
if (httpGet != null) {
httpGet.releaseConnection();
}
}
return body;
}
public static String sendPostJson(String url, String json, Map<String, String> header) throws Exception {
HttpPost httpPost = null;
String body = "";
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
httpPost = new HttpPost(url);
httpPost.setHeader("Content-type", "application/json; charset=utf-8");
httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
if (header != null) {
for (Entry<String, String> entry : header.entrySet()) {
httpPost.setHeader(entry.getKey(), entry.getValue());
}
}
StringEntity entity = new StringEntity(json, Charset.forName("UTF-8"));
entity.setContentEncoding("UTF-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
HttpResponse response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
throw new RuntimeException("恳求失利");
} else {
body = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
throw e;
} finally {
if (httpPost != null) {
httpPost.releaseConnection();
}
}
return body;
}
public static String sendPostForm(String url, Map<String, String> params, Map<String, String> header) throws Exception {
HttpPost httpPost = null;
String body = "";
try {
CloseableHttpClient httpClient = HttpClients.createDefault();
httpPost = new HttpPost(url);
httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");
httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
if (header != null) {
for (Entry<String, String> entry : header.entrySet()) {
httpPost.setHeader(entry.getKey(), entry.getValue());
}
}
List<NameValuePair> nvps = new ArrayList<>();
if (params != null) {
for (Entry<String, String> entry : params.entrySet()) {
nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
}
//设置参数到恳求对象中
httpPost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
HttpResponse response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
throw new RuntimeException("恳求失利");
} else {
body = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
throw e;
} finally {
if (httpPost != null) {
httpPost.releaseConnection();
}
}
return body;
}
}
二.百度地图敞开渠道完成
1. 运用预备
阅览文档一般IP定位 | 百度地图API SDK (baidu.com),成为开发者,获取服务密钥(AK)
2. 封装实体类
/**
* @author 摆渡人
* @description 地址解析的成果
* @date 2023/7/10 20:29
*/
@Data
public class IpAnalyzeBaiduResponse implements Serializable {
/**
* 回来状况码
*/
private Integer status;
/**
* 过错信息
*/
private String message;
/**
* 地址
*/
private String address;
/**
* 具体内容
*/
private IpAnalyzeContent content;
}
/**
* @author 摆渡人
* @description 具体内容
* @date 2023/7/10 20:42
*/
@Data
public class IpAnalyzeContent implements Serializable {
/**
* 简要地址
*/
private String address;
/**
* 具体地址信息
*/
@JsonProperty("address_detail")
private IpAnalyzeAddressDetail addressDetail;
/**
* 百度经纬度坐标值
*/
@JsonProperty("point")
private IpAnalyzePoint point;
}
/**
* @author 摆渡人
* @description 具体地址信息
* @date 2023/7/10 20:41
*/
@Data
public class IpAnalyzeAddressDetail implements Serializable {
/**
* 城市
*/
private String city;
/**
* 百度城市代码
*/
@JsonProperty("city_code")
private Integer cityCode;
/**
* 区县
*/
private String district;
/**
* 省份
*/
private String province;
/**
* 大街
*/
private String street;
/**
* 门地址
*/
@JsonProperty("street_number")
private String streetNumber;
}
/**
* @author 摆渡人
* @description 百度经纬度坐标值
* @date 2023/7/10 20:43
*/
@Data
public class IpAnalyzePoint implements Serializable {
private String x;
private String y;
}
3.完成接口IPlatFormIpAnalyzeService
/**
* @author 摆渡人
* @description 百度地图开发渠道完成类
* @date 2023/7/10 20:27
*/
@Slf4j
public class PlatformBaiduService implements IPlatFormIpAnalyzeService {
/**
* 呼应成功状况码
*/
private final Integer OK_CODE = 0;
@Value("${platform.conf.baidu.ak}")
public String ak;
@Override
public IpAnalyzeResponse ipAnalyze(String ip) {
String URL = "http://api.map.baidu.com/location/ip";
Map<String,String> params = new HashMap<>();
params.put("ip", ip);
params.put("ak", ak);
params.put("coor", "bd09ll");
try {
String json = SmartHttpUtil.sendGet(URL, params, null);
IpAnalyzeBaiduResponse response = JSONUtil.toBean(json, IpAnalyzeBaiduResponse.class);
if(OK_CODE.equals(response.getStatus())) {
return IpAnalyzeResponse.builder()
.status(response.getStatus() + "")
.successFlag(true)
.msg(response.getMessage())
.province(response.getContent().getAddressDetail().getProvince())
.city(response.getContent().getAddressDetail().getCity())
.x(Double.parseDouble(response.getContent().getPoint().getX()))
.y(Double.parseDouble(response.getContent().getPoint().getY()))
.build();
}
return IpAnalyzeResponse.builder()
.status(response.getStatus() + "")
.successFlag(false)
.msg(response.getMessage())
.build();
} catch (Exception e) {
return IpAnalyzeResponse.builder()
.status("500")
.successFlag(false)
.msg(e.getMessage())
.build();
}
}
}
三.高德地图敞开渠道完成
1. 运用预备
依据文档获取Key-创立工程-开发指南-Web服务 API|高德地图API (amap.com)指引获取key
2. 封装实体类
/**
* @author 摆渡人
* @description 高德地图ip定位呼应类
* @date 2023/7/14 0:00
*/
@Data
public class IpAnalyzeGaodeResponse {
/**
* 回来成果状况值,值为0或1,0表明失利;1表明成功
*/
private String status;
/**
* 回来状况阐明,status为0时,info回来过错原因,不然回来“OK
*/
private String info;
/**
* 回来状况阐明,10000代表正确,详情参阅info状况表
*/
private String infocode;
/**
* 若为直辖市则显示直辖市称号;
*
* 假如在局域网 IP网段内,则回来“局域网”;
*
* 不合法IP以及国外IP则回来空
*/
private String province;
/**
* 若为直辖市则显示直辖市称号;
*
* 假如为局域网网段内IP或许不合法IP或国外IP,则回来空
*/
private String city;
/**
* 城市的adcode编码
*/
private String adcode;
/**
* 所在城市矩形区域规模,所在城市规模的左下右上对标对,如:116.0119343,39.66127144;116.7829835,40.2164962
*/
private String rectangle;
}
3.完成接口IPlatFormIpAnalyzeService
/**
* @author 摆渡人
* @description 高德地图敞开渠道ip定位完成类
* @date 2023/7/13 23:56
*/
@Slf4j
public class PlatformGaodeService implements IPlatFormIpAnalyzeService {
/**
* 呼应成功状况码
*/
private final String OK_CODE = "1";
@Value("${platform.conf.gaode.key}")
private String key;
@Override
public IpAnalyzeResponse ipAnalyze(String ip) {
String URL = "https://restapi.amap.com/v3/ip";
Map<String,String> params = new HashMap<>();
params.put("key",key);
params.put("ip",ip);
try {
String json = SmartHttpUtil.sendGet(URL, params, null);
IpAnalyzeGaodeResponse response = JSONUtil.toBean(json, IpAnalyzeGaodeResponse.class);
if(OK_CODE.equals(response.getStatus())) {
IpAnalyzeResponse ipAnalyzeResponse = IpAnalyzeResponse.builder()
.status(response.getInfocode())
.successFlag(true)
.msg(response.getInfo())
.province(response.getProvince())
.city(response.getCity())
.build();
// 对rectangle进行解析
String rectangle = response.getRectangle();
String[] split = rectangle.split(";|,");
if(split.length == 1) {
ipAnalyzeResponse.setSuccessFlag(false);
ipAnalyzeResponse.setStatus("0");
ipAnalyzeResponse.setMsg("ip[" + ip + "]不合法");
return ipAnalyzeResponse;
}
ipAnalyzeResponse.setX((Double.parseDouble(split[0]) + Double.parseDouble(split[2])) / 2.0);
ipAnalyzeResponse.setY((Double.parseDouble(split[1]) + Double.parseDouble(split[3])) / 2.0);
return ipAnalyzeResponse;
}
return IpAnalyzeResponse.builder()
.status(response.getInfocode())
.successFlag(false)
.msg(response.getInfo())
.build();
} catch (Exception e) {
e.printStackTrace();
return IpAnalyzeResponse.builder()
.status(“500”)
.successFlag(false)
.msg(e.getMessage())
.build();
}
}
}
四. 运用
1.装备参数
# 第三方敞开渠道的服务
platform:
# 现在支持百度[baidu] 高德[gaode]
mode: # 您挑选的服务
# 相关装备
conf:
# 百度
baidu:
ak: # 您的百度地图敞开渠道服务密钥(AK)
# 高德
gaode:
key: # 您的高德地图敞开渠道key
2.装备类
依据装备参数中
platform.mode
的值挑选接口IPlatFormIpAnalyzeService
的一个服务类注入Spring容器中去
/**
* @author 摆渡人
* @description 接口完成类装备
* @date 2023/7/14 0:16
*/
@Configuration
public class PlatFormConfig {
@Bean
@ConditionalOnProperty(prefix = "platform",name = "mode",havingValue = "baidu")
public IPlatFormIpAnalyzeService initBaiduIpAnalyzeService() {
return new PlatformBaiduService();
}
@Bean
@ConditionalOnProperty(prefix = "platform",name = "mode",havingValue = "gaode")
public IPlatFormIpAnalyzeService initGaodeIpAnalyzeService() {
return new PlatformGaodeService();
}
}
3.单元测试
/**
* @author 摆渡人
* @description
* @date 2023/7/14 0:23
*/
@Slf4j
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class PlatformTest {
@Autowired
private IPlatFormIpAnalyzeService platFormIpAnalyzeService;
@Test
public void testIpAnalyzeService() {
log.info("解析成果:{}",platFormIpAnalyzeService.ipAnalyze(null));
log.info("解析成果:{}",platFormIpAnalyzeService.ipAnalyze(""));
log.info("解析成果:{}",platFormIpAnalyzeService.ipAnalyze("127.0.0.1"));
log.info("解析成果:{}",platFormIpAnalyzeService.ipAnalyze("114.247.50.2"));
log.info("解析成果:{}",platFormIpAnalyzeService.ipAnalyze("219.137.148.0"));
log.info("解析成果:{}",platFormIpAnalyzeService.ipAnalyze("114.80.166.240"));
}
}
运转成果如下:
-
platform.mode=gaode
[2023-07-22 22:42:40,426][INFO ][main] 解析成果:IpAnalyzeResponse(status=0, successFlag=false, msg=ip[null]不合法, province=[], city=[], x=null, y=null) (PlatformTest.java:26) [2023-07-22 22:42:40,662][INFO ][main] 解析成果:IpAnalyzeResponse(status=0, successFlag=false, msg=ip[]不合法, province=[], city=[], x=null, y=null) (PlatformTest.java:27) [2023-07-22 22:42:40,898][INFO ][main] 解析成果:IpAnalyzeResponse(status=0, successFlag=false, msg=ip[127.0.0.1]不合法, province=[], city=[], x=null, y=null) (PlatformTest.java:28) [2023-07-22 22:42:41,132][INFO ][main] 解析成果:IpAnalyzeResponse(status=10000, successFlag=true, msg=OK, province=北京市, city=北京市, x=116.3974589, y=39.93888382) (PlatformTest.java:29) [2023-07-22 22:42:41,365][INFO ][main] 解析成果:IpAnalyzeResponse(status=10000, successFlag=true, msg=OK, province=广东省, city=广州市, x=113.3893937, y=23.15653812) (PlatformTest.java:30) [2023-07-22 22:42:41,600][INFO ][main] 解析成果:IpAnalyzeResponse(status=10000, successFlag=true, msg=OK, province=上海市, city=上海市, x=121.4767528, y=31.224348955) (PlatformTest.java:31)
-
platform.mode=baidu
[2023-07-22 22:40:48,774][INFO ][main] 解析成果:IpAnalyzeResponse(status=2, successFlag=false, msg=Request Parameter Error:ip illegal, province=null, city=null, x=null, y=null) (PlatformTest.java:26) [2023-07-22 22:40:48,825][INFO ][main] 解析成果:IpAnalyzeResponse(status=0, successFlag=true, msg=null, province=广东省, city=广州市, x=113.27143134, y=23.13533631) (PlatformTest.java:27) [2023-07-22 22:40:48,862][INFO ][main] 解析成果:IpAnalyzeResponse(status=1, successFlag=false, msg=Internal Service Error:ip[127.0.0.1] loc failed, province=null, city=null, x=null, y=null) (PlatformTest.java:28) [2023-07-22 22:40:48,899][INFO ][main] 解析成果:IpAnalyzeResponse(status=0, successFlag=true, msg=null, province=北京市, city=北京市, x=116.4133837, y=39.91092455) (PlatformTest.java:29) [2023-07-22 22:40:48,949][INFO ][main] 解析成果:IpAnalyzeResponse(status=0, successFlag=true, msg=null, province=广东省, city=广州市, x=113.27143134, y=23.13533631) (PlatformTest.java:30) [2023-07-22 22:40:48,988][INFO ][main] 解析成果:IpAnalyzeResponse(status=0, successFlag=true, msg=null, province=上海市, city=上海市, x=121.48789949, y=31.24916171) (PlatformTest.java:31)
五. 其它
1.nginx反向署理后,服务端获取的是nginx所在服务器的ip
可以参阅我的另一篇文章,nginx反向署理后服务端获取客户端真实ip