项目中有一个登录邮箱提醒的功能,需要根据IP地址获取定位信息,从而更好地提示用户账号登录的所在地。为此,花费了一些时间来实现这个功能。
在CSDN搜索了一下,发现关于获取定位的文章说明都不够详细,于是决定自己创作一篇文章,希望能够帮助到有需求的小伙伴~
因为博主日常生活中用的导航APP就是百度地图,所以使用百度地图的API来实现这个功能。
目录
百度地图开放平台
开始前的准备工作
学习官网API文档
修改API的AK配置
Java代码获取定位
通过官网生成代码
运行生成的代码
创建接口返回对象
Point.java
Location.java
Content.java
AddressDetail.java
将JSON字符串转成Location对象
代码的最后优化
百度地图开放平台
本篇文章都是围绕百度地图的开放平台官网的相关说明来展开的,访问下面的链接
百度地图开放平台 | 百度地图API SDK | 地图开发https://lbsyun.baidu.com/index.php?title=%E9%A6%96%E9%A1%B5这是开放平台的首页截图
开始前的准备工作
点击上方导航菜单的开发文档-Web服务API总览
紧接着,点击左侧菜单中的定位,点击普通IP定位
然后根据开发文档的提示完成步骤1、2、3
学习官网API文档
最后点击步骤4,开始使用百度地图的API
如上图,文档已经给出了API的接口地址和参数说明,以及API接口返回的参数格式。
{
"address": "CN|北京市|北京市|None|None|100|91",
"content": {
"address": "北京市",
"address_detail": {
"adcode": "110000",
"city": "北京市",
"city_code": 131,
"district": "",
"province": "北京市",
"street": "",
"street_number": ""
},
"point": {
"x": "116.41338370",
"y": "39.91092455"
}
},
"status": 0
}
修改API的AK配置
在这里,需要修改一下创建的应用的相关设置。
点击上图的设置按钮来到这个页面,然后拉到页面的底部,修改应用的请求校验方式为sn校验方式,然后点击提交按钮。
把AK复制到示例代码的输入框中,点击确认按钮自动生成获取定位的Java代码,可以说非常方便。
Java代码获取定位
通过官网生成代码
新建一个类SearchHttpAK,把示例代码复制到SearchHttpAK类里,复制按钮在下图红框内。
调整之后的代码如下(真实的AK和SK都已经被博主替换了,这里的5个x改成自己的AK和SK就行了)。
package cn.edu.sgu.www.authority.util;
import org.springframework.web.util.UriUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 选择AK使用SN校验:
* @author heyunlin
* @version 1.0
*/
public class SearchHttpSN {
public static String AK = "xxxxx";
public static String SK = "xxxxx";
public static String URL = "https://api.map.baidu.com/location/ip?";
public static void main(String[] args) throws Exception {
SearchHttpSN snCal = new SearchHttpSN();
Map<String, String> params = new LinkedHashMap<>(4);
params.put("ip", "111.206.214.37");
params.put("coor", "bd09ll");
params.put("ak", AK);
params.put("sn", snCal.caculateSn());
snCal.requestGetSN(URL, params);
}
/**
* 选择了ak,使用SN校验:
* 根据您选择的AK已为您生成调用代码
* 检测您当前的AK设置了sn检验,本示例中已为您生成sn计算代码
* @param strUrl
* @param param
* @throws Exception
*/
public void requestGetSN(String strUrl, Map<String, String> param) throws Exception {
if (strUrl == null || strUrl.length() <= 0 || param == null || param.size() <= 0) {
return;
}
StringBuilder queryString = new StringBuilder();
queryString.append(strUrl);
for (Map.Entry<?, ?> pair : param.entrySet()) {
queryString.append(pair.getKey()).append("=");
// 第一种方式使用的 jdk 自带的转码方式 第二种方式使用的 spring 的转码方法 两种均可
// queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8")).append("&");
}
if (queryString.length() > 0) {
queryString.deleteCharAt(queryString.length() - 1);
}
java.net.URL url = new URL(queryString.toString());
System.out.println(queryString.toString());
URLConnection httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.connect();
InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
BufferedReader reader = new BufferedReader(isr);
StringBuilder buffer = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
reader.close();
isr.close();
System.out.println("SN: " + buffer);
}
public String caculateSn() throws UnsupportedEncodingException,
NoSuchAlgorithmException {
SearchHttpSN snCal = new SearchHttpSN();
// 计算sn跟参数对出现顺序有关,get请求请使用LinkedHashMap保存<key,value>,该方法根据key的插入顺序排序;post请使用TreeMap保存<key,value>,该方法会自动将key按照字母a-z顺序排序。
// 所以get请求可自定义参数顺序(sn参数必须在最后)发送请求,但是post请求必须按照字母a-z顺序填充body(sn参数必须在最后)。
// 以get请求为例:http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak,paramsMap中先放入address,再放output,然后放ak,放入顺序必须跟get请求中对应参数的出现顺序保持一致。
Map<String, String> paramsMap = new LinkedHashMap<>(3);
paramsMap.put("ip", "111.206.214.37");
paramsMap.put("coor", "bd09ll");
paramsMap.put("ak", AK);
// 调用下面的toQueryString方法,对LinkedHashMap内所有value作utf8编码,拼接返回结果address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourak
String paramsStr = snCal.toQueryString(paramsMap);
// 对paramsStr前面拼接上/geocoder/v2/?,后面直接拼接yoursk得到/geocoder/v2/?address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourakyoursk
String wholeStr = new String("/location/ip?" + paramsStr + SK);
System.out.println(wholeStr);
// 对上面wholeStr再作utf8编码
String tempStr = URLEncoder.encode(wholeStr, "UTF-8");
// 调用下面的MD5方法得到最后的sn签名
String sn = snCal.MD5(tempStr);
System.out.println(sn);
return sn;
}
// 对Map内所有value作utf8编码,拼接返回结果
public String toQueryString(Map<?, ?> data) {
StringBuilder queryString = new StringBuilder();
for (Map.Entry<?, ?> pair : data.entrySet()) {
queryString.append(pair.getKey()).append("=");
// 第一种方式使用的 jdk 自带的转码方式 第二种方式使用的 spring 的转码方法 两种均可
// queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8")).append("&");
}
if (queryString.length() > 0) {
queryString.deleteCharAt(queryString.length() - 1);
}
return queryString.toString();
}
// 来自stackoverflow的MD5计算方法,调用了MessageDigest库函数,并把byte数组结果转换成16进制
public String MD5(String md5) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] array = md.digest(md5.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : array) {
sb.append(Integer.toHexString((b & 0xFF) | 0x100), 1, 3);
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException ignored) { }
return null;
}
}
注意,不要修改这里的map的放入顺序,否则请求接口会报错~
Map<String, String> params = new LinkedHashMap<>(4);
params.put("ip", "111.206.214.37");
params.put("coor", "bd09ll");
params.put("ak", AK);
params.put("sn", snCal.caculateSn());
运行生成的代码
然后运行一下代码,如果能看到下面这样的运行结果,恭喜你,你已经成功了一大半~
不要纠结获取到的位置信息里很多16进制,因为马上就要把他转成一个自定义的实体类
创建接口返回对象
根据这个API返回的JSON格式字符串的结构,创建4个对应的实体类
Point.java
import lombok.Data;
/**
* @author heyunlin
* @version 1.0
*/
@Data
public class Point {
private String x;
private String y;
}
Location.java
import lombok.Data;
/**
* @author heyunlin
* @version 1.0
*/
@Data
public class Location {
private Integer status;
private String address;
private Content content;
}
Content.java
import lombok.Data;
/**
* @author heyunlin
* @version 1.0
*/
@Data
public class Content {
private Point point;
private String address;
private AddressDetail address_detail;
}
AddressDetail.java
import lombok.Data;
/**
* @author heyunlin
* @version 1.0
*/
@Data
public class AddressDetail {
private String adcode;
private String city;
private String city_code;
private String province;
private String district;
private String street;
private String street_number;
}
将JSON字符串转成Location对象
修改一下部分方法的名称,然后把字符串通过fastjson的API转成Location对象
package cn.edu.sgu.www.authority.util;
import cn.edu.sgu.www.authority.location.Location;
import com.alibaba.fastjson.JSON;
import org.springframework.web.util.UriUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 选择AK使用SN校验:
* @author heyunlin
* @version 1.0
*/
public class SearchHttpSN {
public static String AK = "xxxxx";
public static String SK = "xxxxx";
public static String URL = "https://api.map.baidu.com/location/ip?";
public static void main(String[] args) throws Exception {
String ip = "111.206.214.37";
String location = getLocation(ip);
System.out.println(JSON.parseObject(location, Location.class));
}
public static String getLocation(String ip) throws Exception {
Map<String, String> params = new LinkedHashMap<>(4);
params.put("ip", ip);
params.put("coor", "bd09ll");
params.put("ak", AK);
params.put("sn", caculateSn());
return getLocation(URL, params);
}
/**
* 选择了ak,使用SN校验:
* 根据您选择的AK已为您生成调用代码
* 检测您当前的AK设置了sn检验,本示例中已为您生成sn计算代码
* @param strUrl
* @param param
* @throws Exception
*/
public static String getLocation(String strUrl, Map<String, String> param) throws Exception {
if (strUrl == null || strUrl.length() <= 0 || param == null || param.size() <= 0) {
return null;
}
StringBuilder queryString = new StringBuilder();
queryString.append(strUrl);
for (Map.Entry<?, ?> pair : param.entrySet()) {
queryString.append(pair.getKey()).append("=");
// 第一种方式使用的 jdk 自带的转码方式 第二种方式使用的 spring 的转码方法 两种均可
// queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8")).append("&");
}
if (queryString.length() > 0) {
queryString.deleteCharAt(queryString.length() - 1);
}
java.net.URL url = new URL(queryString.toString());
System.out.println(queryString);
URLConnection httpConnection = url.openConnection();
httpConnection.connect();
InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
BufferedReader reader = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
isr.close();
return sb.toString();
}
public static String caculateSn() throws UnsupportedEncodingException {
// 计算sn跟参数对出现顺序有关,get请求请使用LinkedHashMap保存<key,value>,该方法根据key的插入顺序排序;post请使用TreeMap保存<key,value>,该方法会自动将key按照字母a-z顺序排序。
// 所以get请求可自定义参数顺序(sn参数必须在最后)发送请求,但是post请求必须按照字母a-z顺序填充body(sn参数必须在最后)。
// 以get请求为例:http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak,paramsMap中先放入address,再放output,然后放ak,放入顺序必须跟get请求中对应参数的出现顺序保持一致。
Map<String, String> paramsMap = new LinkedHashMap<>(3);
paramsMap.put("ip", "111.206.214.37");
paramsMap.put("coor", "bd09ll");
paramsMap.put("ak", AK);
// 调用下面的toQueryString方法,对LinkedHashMap内所有value作utf8编码,拼接返回结果address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourak
String paramsStr = toQueryString(paramsMap);
// 对paramsStr前面拼接上/geocoder/v2/?,后面直接拼接yoursk得到/geocoder/v2/?address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourakyoursk
String wholeStr = new String("/location/ip?" + paramsStr + SK);
System.out.println(wholeStr);
// 对上面wholeStr再作utf8编码
String tempStr = URLEncoder.encode(wholeStr, "UTF-8");
// 调用下面的MD5方法得到最后的sn签名
return MD5(tempStr);
}
// 对Map内所有value作utf8编码,拼接返回结果
public static String toQueryString(Map<?, ?> data) {
StringBuilder queryString = new StringBuilder();
for (Map.Entry<?, ?> pair : data.entrySet()) {
queryString.append(pair.getKey()).append("=");
// 第一种方式使用的 jdk 自带的转码方式 第二种方式使用的 spring 的转码方法 两种均可
// queryString.append(URLEncoder.encode((String) pair.getValue(), "UTF-8").replace("+", "%20") + "&");
queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8")).append("&");
}
if (queryString.length() > 0) {
queryString.deleteCharAt(queryString.length() - 1);
}
return queryString.toString();
}
// 来自stackoverflow的MD5计算方法,调用了MessageDigest库函数,并把byte数组结果转换成16进制
public static String MD5(String md5) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] array = md.digest(md5.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : array) {
sb.append(Integer.toHexString((b & 0xFF) | 0x100), 1, 3);
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException ignored) { }
return null;
}
}
运行代码,效果如下:
在使用的时候,只需要传入IP地址即可获取到定位信息~
代码的最后优化
上面的代码已经是进行了一定的优化,除此之外,可以把AK和SK配置到application.yml中,可以动态地修改。最后重命名一下这个类,改成类似LocationUtils这样的名字。
好了,文章就分享到这里了,看完如果觉得对你有所帮助,不要忘了点赞+收藏哦~