獲取用戶ip所在城市
整體流程圖

獲取當(dāng)前登錄用戶所在城市,是一個(gè)非常常見的需求,在很多業(yè)務(wù)場景中用到。
比如:導(dǎo)航的定位功能默認(rèn)選擇的城市,或者一些防盜系統(tǒng)中識別用戶兩次登錄的城市不一樣的會有報(bào)警提示。
下載geoip2數(shù)據(jù)庫
geoip2是國外的一個(gè)免費(fèi)的IP庫,只要注冊賬號就能下載IP數(shù)據(jù)庫了。
官網(wǎng)地址:https://www.maxmind.com/。
其他下載:https://kohler.lanzouo.com/i3Fkb36wobuh
將IP庫保存到電腦的某個(gè)目錄下

之后使用絕對路徑

引入相關(guān)依賴
引入geoip相關(guān)的依賴包:
<dependency>
<groupId>com.maxmind.geoip2</groupId>
<artifactId>geoip2</artifactId>
<version>2.16.1</version>
</dependency>
增加獲取城市接口
增加一個(gè)根據(jù)ip獲取所在城市的接口。
創(chuàng)建一個(gè)GeoIpController類:
@Tag( name= "geoip操作", description = "geoip操作")
@RestController
@RequestMapping("/v1/web/geoip")
@Validated
public class GeoIpController {
@Autowired
private GeoIpHelper geoIpHelper;
/**
* 根據(jù)ip獲取所在城市
*
* @param ip ip地址
* @return 城市
*/
@NoLogin
@Operation(summary = "根據(jù)ip獲取所在城市", description = "根據(jù)ip獲取所在城市")
@GetMapping("/getCity")
public CityDTO getCity(@RequestParam(value = "ip") String ip) {
return geoIpHelper.getCity(ip);
}
}
這個(gè)類中只包含了getCity這一個(gè)接口,該接口就是通過ip獲取所在國家、省份和城市。
創(chuàng)建GeoIpHelper類:
@Slf4j
@Component
public class GeoIpHelper {
private static final String GEO_IP_FILE_NAME = "GeoLite2-City.mmdb";
@Value("${shop.mgt.geoIpFilePath:}")
private String geoIpFilePath;
@Value("${shop.mgt.taobaoIpUrl:}")
private String taobaoIpUrl;
@Value("${shop.mgt.taobaoIpRequestOff:false}")
private Boolean taobaoIpRequestOff;
@Autowired
private HttpHelper httpHelper;
/**
* 根據(jù)ip獲取所在城市
*
* @param ip ip地址
* @return 城市
*/
public CityDTO getCity(String ip) {
CityDTO cityFromGeoIp = getCityFromGeoIp(ip);
if (Objects.nonNull(cityFromGeoIp) && Objects.nonNull(cityFromGeoIp.getCity())) {
return cityFromGeoIp;
}
if (taobaoIpRequestOff) {
return null;
}
return getCityFromApi(ip);
}
private CityDTO getCityFromGeoIp(String ip) {
String fileUrl = getFileUrl();
File file = new File(fileUrl);
if (!file.exists()) {
log.warn(String.format("%s文件不存在", fileUrl));
return null;
}
try {
DatabaseReader reader = new DatabaseReader.Builder(file).build();
//解析IP地址
InetAddress ipAddress = InetAddress.getByName(ip);
// 獲取查詢結(jié)果
CityResponse response = reader.city(ipAddress);
if (response == null) {
return null;
}
// 國家
String country = response.getCountry().getNames().get("zh-CN");
// 省份
String province = response.getMostSpecificSubdivision().getNames().get("zh-CN");
//城市
String city = response.getCity().getNames().get("zh-CN");
return new CityDTO(ip, country, province, city);
} catch (Exception e) {
log.error("從GeoIp庫中獲取城市失敗,原因:", e);
}
return null;
}
private CityDTO getCityFromApi(String ip) {
String url = String.format(taobaoIpUrl, ip);
TaoboCityEntity taoboCityEntity = httpHelper.doGet(url, TaoboCityEntity.class);
if (Objects.nonNull(taoboCityEntity)) {
TaoboCityEntity.TaoboAreaEntity data = taoboCityEntity.getData();
if (Objects.nonNull(data)) {
return new CityDTO(ip, data.getCountry(), data.getRegion(), data.getCity());
}
}
return null;
}
private String getFileUrl() {
return geoIpFilePath + "/" + GEO_IP_FILE_NAME;
}
}
這個(gè)類主要是來用讀取IP庫的數(shù)據(jù),通過ip查詢國家、省份和城市。
由于ip庫有可能不全,或者不是最新的數(shù)據(jù)。可能會導(dǎo)致根據(jù)ip獲取不到我們想要的數(shù)據(jù)的情況。
因此需要做兼容處理,如果從ip庫查詢不到數(shù)據(jù),則再調(diào)用一下遠(yuǎn)程接口獲取數(shù)據(jù)。
調(diào)用淘寶接口
創(chuàng)建HttpHelper類:
@Slf4j
@Component
public class HttpHelper {
@Autowired
private RestTemplate restTemplate;
/**
* 發(fā)送get請求
*
* @param url url地址
* @param tClass 返回值實(shí)體
* @param <T> 泛型
* @return 返回值實(shí)體
*/
public <T> T doGet(String url, Class<T> tClass) {
return restTemplate.getForObject(url, tClass);
}
}
定義了doGet方法獲取用戶請求。
創(chuàng)建了RestTemplateConfig類:
@Configuration
public class RestTemplateConfig {
@Value("${shop.mgt.rest.template.connectTimeout:3000}")
private int connectTimeout;
@Value("${shop.mgt.rest.template.readTimeout:50000}")
private int readTimeout;
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(connectTimeout);
factory.setReadTimeout(readTimeout);
return factory;
}
/**
* 忽略證書配置
*/
public static HttpComponentsClientHttpRequestFactory generateHttpRequestFactory()
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
TrustStrategy acceptingTrustStrategy = (x509Certificates, authType) -> true;
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
new NoopHostnameVerifier());
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setSSLSocketFactory(connectionSocketFactory);
CloseableHttpClient httpClient = httpClientBuilder.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setHttpClient(httpClient);
return factory;
}
}
這個(gè)類是一個(gè)配置類,主要配置了RestTemplate的一些參數(shù),以及https請求忽略證書的情況。
如果不忽略證書,有些情況下,比如:調(diào)用測試環(huán)境的請求,使用的不是有效的證書,會請求失敗。
application.yml文件中增加配置:
shop:
mgt:
geoIpFilePath: D:\workplace\SpringBoot\kailong_shop\shop_business\src\main\resources\files\geoip2
taobaoIpUrl: https://ip.taobao.com/outGetIpInfo?ip=%s&accessKey=alibaba-inc
其中accessKey使用的淘寶的測試key。
測試
代碼開發(fā)好之后,接下來進(jìn)行測試。
在瀏覽器上訪問:http://localhost:8011/v1/web/geoip/getCity?ip=123.245.11.177(我的測試路徑)
返回了正確的城市

說明根據(jù)ip獲取所在城市的功能OK了。
需要特別注意的是目前調(diào)用淘寶的測試接口,每天有數(shù)量限制,達(dá)到一定測試就會返回失敗。
也可以換成其他平臺的接口,或者申請一個(gè)付費(fèi)的accessKey。


浙公網(wǎng)安備 33010602011771號