Java调用百度API出现{"status":211,"message":"APP SN校验失败"}
{“status”:211,“message”:“APP SN校验失败”}
1、百度官方提供了C#、Java、python、php等版本的sn签名计算算法:
sn计算算法
2、本人在接入API接口过程中发现官方demo不太完善。以下是本人的Java代码:
package cn.maps.baidu.util;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Map.Entry;
public class SnCalUtil {
public SnCalUtil() {
}
/**
* 计算sn跟参数对出现顺序有关(参数对放入顺序必须跟请求中对应参数的出现顺序保持一致):
* get请求请使用LinkedHashMap保存<key,value>,该方法根据key的插入顺序排序;
* post请使用TreeMap保存<key,value>,该方法会自动将key按照字母a-z顺序排序。
* 所以get请求可自定义参数顺序(sn参数必须在最后)发送请求,
* 但是post请求必须按照字母a-z顺序填充body(sn参数必须在最后)。
*
* @param url 请求路径url
* @param paramsMap 请求参数对
* @param sk ak对应sk秘钥
* @return sn签名
* @throws UnsupportedEncodingException
*/
public String getSn(String url, Map<?, ?> paramsMap, String sk) throws UnsupportedEncodingException {
String paramsStr = toQueryString(paramsMap);
url = url.replaceFirst("http://api.map.baidu.com", "").trim();
url = url.replaceFirst("http://yingyan.baidu.com", "").trim();
System.out.println("************************Calculate Sn >>>>>url:" + url);
//url在计算sn签名的时候,不管是get请求还是post请求,必须带?
String wholeStr = url + paramsStr + sk;
System.out.println("************************Calculate Sn >>>>>wholeStr:" + wholeStr);
//对上面wholeStr再作utf8编码
String tempStr = URLEncoder.encode(wholeStr, "UTF-8");
System.out.println("************************Calculate Sn >>>>>tempStr:" + tempStr);
//调用下面的MD5方法得到最后的sn签名
String sn = MD5(tempStr);
System.out.println("************************Calculate Sn >>>>>Sn:" + sn);
return sn;
}
/**
* 对请求参数Map内所有value作utf8编码,拼接返回结果
*
* @param data 请求参数对
* @return str 字符串
* @throws UnsupportedEncodingException 异常
*/
public String toQueryString(Map<?, ?> data) throws UnsupportedEncodingException {
StringBuffer queryString = new StringBuffer();
for (Entry<?, ?> pair : data.entrySet()) {
queryString.append(pair.getKey()).append("=");
String ss[] = String.valueOf(pair.getValue()).split(",");
if (ss.length>1){
for(String s:ss){
queryString.append(URLEncoder.encode(s, "UTF-8")).append(",");
}
queryString.deleteCharAt(queryString.length()-1);
queryString.append("&");
}else {
queryString.append(URLEncoder.encode(String.valueOf(pair.getValue()), "UTF-8"));
queryString.append("&");
}
}
if (queryString.length() > 0) {
queryString.deleteCharAt(queryString.length() - 1);
}
return queryString.toString();
}
/**
* 来自stackoverflow的MD5计算方法,调用了MessageDigest库函数,并把byte数组结果转换成16进制
*
* @param str 要计算的字符串
* @return sn
*/
public String MD5(String str) {
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] array = md.digest(str.getBytes());
StringBuffer sb = new StringBuffer();
for (byte b : array) {
sb.append(Integer.toHexString((b & 0xFF) | 0x100), 1, 3);
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException ignored) {
}
return null;
}
}
注意:
(1)因为我是从配置文件中读取百度API接口的url,计算sn签名时候是从.com以后开始,所以我的代码有如下转换:
url = url.replaceFirst("http://api.map.baidu.com", "").trim();
url = url.replaceFirst("http://yingyan.baidu.com", "").trim();
##百度Web服务API-逆地理编码接口地址(GET请求)
maps.baidu.position.url=http://api.map.baidu.com/reverse_geocoding/v3/?
##百度鹰眼轨迹服务——终端管理类——entity的创建接口(POST请求)
maps.baidu.yingyan.terminals.entity.add.url=http://yingyan.baidu.com/api/v3/entity/add?
url在计算sn签名的时候,不管是get请求还是post请求,必须带?,否则会出现如题所示的{“status”:211,“message”:“APP SN校验失败”}
(2)官方提供的toQueryString(Map<?, ?> data)方法中做了修改,对“,”转义处理。
for (Entry<?, ?> pair : data.entrySet()) {
queryString.append(pair.getKey()).append("=");
String ss[] = String.valueOf(pair.getValue()).split(",");
if (ss.length>1){
for(String s:ss){
queryString.append(URLEncoder.encode(s, "UTF-8")).append(",");
}
queryString.deleteCharAt(queryString.length()-1);
queryString.append("&");
}else {
queryString.append(URLEncoder.encode(String.valueOf(pair.getValue()), "UTF-8"));
queryString.append("&");
}
}
或者在HttpClient发送get请求时候替换参数里的逗号为“%2C”也可以。