项目中有一个登录邮箱提醒的功能,需要根据ip地址获取定位信息,从而更好地提示用户账号登录的所在地。为此,花费了一些时间来实现这个功能。
搜索了一下,发现关于获取定位的文章说明都不够详细,于是决定自己创作一篇文章,希望能够帮助到有需求的小伙伴~
因为博主日常生活中用的导航app就是百度地图,所以使用百度地图的api来实现这个功能。
百度地图开放平台
本篇文章都是围绕百度地图的开放平台官网的相关说明来展开的,访问下面的链接
百度地图开放平台 | 百度地图api sdk | 地图开发 这是开放平台的首页截图
开始前的准备工作
点击上方导航菜单的开发文档-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这样的名字。
总结
到此这篇关于java通过百度地图api获取定位(普通ip定位)的文章就介绍到这了,更多相关java百度地图api获取定位内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论