API概述

概述

千寻见微Open API支持http和https调用方式,开发者需按一定格式拼装HTTP请求进行API调用,返回结果为JSON格式。

千寻见微Open API支持开发者:

(1)在官网购买服务实例,然后使用Open API进行监测账号、监测对象、监测点的管理。

(2)监测点数据完成解算后,根据监测点查询解算结果。

在完成监测对象和监测点的创建后,开发者可以配置监测点以NtripServer的方式将监测数据发送到千寻见微的接收网关。监测点接收机配置如下:

(1)协议:Ntrip V1.0

(2)千寻见微网关域名:pisa.ntrip.qxwz.com

(3)千寻见微网关端口:8001

(4)挂载点(MountPoint):Open API或者管理控制台创建的监测账号

(5)密码:通过Open API监测点查询接口或者管理控制台监测点管理页面查询

调用格式

千寻见微Open API服务地址:http://openapi.qxwz.com,全面支持http和https,以POST、GET方式进行请求。

一个完整URL请求为:http://openapi.qxwz.com/rest/api_name/sik/key?_sign=xxxxxxxxxxx&param=yyyy,其中

(1) http://openapi.qxwz.com/rest 千寻Open API 服务的统一地址前缀

(2) api_name 接口名称

(3) sik 此处固定为字符串sik,标识key类型为sik

(4) key 具体的sik值

(5) _sign 加签值,根据参数、sik对应的密匙sis、时间戳共同生成;有关_sign 的生成算法请查看下文的【签名机制】和【加签示例】章节

(6) param 参数,若参数为对象,需要进行JSON 序列化

示例

http://openapi.qxwz.com/rest/findmm.point.add/sik/S000000000?_sign=xxxxxxxxxxxxx&request={"objectId":1,"name":"xxx",remark:"xxx"}

签名机制

签名算法使用遵循RFC 2104 规范的HMAC-SHA256 算法,要签名的元素是请求自身的URL、参数和时间戳(毫秒级),由于每个API 请求基本不同,所以签名的结果也不尽相同。RFC2104 规范请参考:http://www.ietf.org/rfc/rfc2104.txt

签名算法

以sis 为种子,对一次请求的URL(URL地址)+Param(参数)+Timestamp (时间戳)进行加签。

(1)从协议域“/rest”开始,至传递参数符“?”结束(不包括?),该部分为需加签的URL 部分。

(2) 从传递参数符“?”开始至参数结束(包括在postContent 中的参数),该部分为需加签的Param 部分,请注意,该部分所有参数需要按照参数名字典序进行从小到大排列。

(3)协议头部中wz-acs-timestamp 为需加签的timestamp 部分。

(4)计算完成后的得到HexString(十六进制字符串),以“_sign=xxxxxxxxx”的参数形式加到URL 中。

加签示例

千寻见微为你提供加签示例,您可以在【相关下载】中下载千寻见微加签Demo,包括加签算法、发送HTTP请求、处理HTTP结果等。请注意如下几点内容:

(1)将sik和sis替换成自身应用的sik和sis

(2)加签Demo依赖如下包,用于http请求,分别是:commons-logging-1.1.1.jar, httpclient-4.3.4.jar,httpcore-4.3.3.jar。如果您使用eclipse,请在项目的Properties->Java Build Path->Libraries中Add JARs中加入依赖包。

(3)签名算法为doHmacSHA2,注意时间戳为当前系统时间,单位为毫秒

(4)需要在请求的Header加入wz-acs-timestamp

(5)请注意中文编码问题,转换成utf-8,规避潜在的乱码问题

import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

public class SignatureDemo {
	public static void main(String[] args) throws Exception{
		String sik = "sik";//请填写您申请的sik
		String sis = "sis";//请填写您申请的sik对应的sis
		String apiName = "findmm.object.list";// 访问的API接口名称
		String apiPath = "/rest/" + apiName + "/" + "sik" + "/"+sik;// API路径,注意没有问号
		// 参数列表(加签的时候需要进行字典序升序排序)
		Map<String, String> paramMap = new HashMap<String, String>();
		String request="{\"pageNo\":1,\"pageSize\":2}";
		paramMap.put("request",request);
// 毫秒级时间戳,一定是毫秒级!重要!!!
		String timestamp = String.valueOf(System.currentTimeMillis());
		String signatureStr = SignatureDemo.doHmacSHA2(apiPath, paramMap, appSecret, timestamp);
		System.out.println("加签值:"+signatureStr);//打印sign值
		
		String qxwzUrl = "http://openapi.qxwz.com";//千寻的服务器地址
		String queryUrl = qxwzUrl + apiPath + "?_sign=" + signatureStr;
		
        //发送http请求,获取返回值
        doHttpPost(queryUrl,paramMap,timestamp);
	}
		
	public static void doHttpPost(String url,Map<String, String> paramMap, String timestamp) throws Exception {
		CloseableHttpClient httpClient = HttpClients.createDefault();	
		HttpPost httpPost = new HttpPost(url);
		httpPost.setHeader("Accept-Encoding", "gzip, deflate");
		httpPost.setHeader("wz-acs-timestamp", timestamp);//此处注意加上时间戳,否则http请求将无效
		List<NameValuePair> nvps = new ArrayList<NameValuePair>();  
		for (Map.Entry<String, String> entry : paramMap.entrySet()){
			if (entry.getValue() == null) continue;
			nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
		}
		httpPost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));//处理中文编码的问题

        
		try {
			CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost); 
			try {  
                HttpEntity entity = response.getEntity();   
                if (entity != null) {    
                    System.out.println("返回值:"+EntityUtils.toString(entity));  
                    /*
                     * 此处针对返回结果进行不同的处理
                     */
                    EntityUtils.consume(entity);  
                }  
            } finally {  
                response.close();  
            } 

		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if (httpPost != null) {
				httpPost.releaseConnection();//释放资源
			}
			if(httpClient != null){
				httpClient.close();//释放资源
			}
		}
	}

	/*
	 * 将字节数组转换成16进制字符串
	 * */
	public static String encodeHexStr(final byte[] bytes){
		if (bytes == null) {
			return null;
		}
		char[] digital  = "0123456789ABCDEF".toCharArray();
		char[] result = new char[bytes.length * 2];
		for (int i = 0; i < bytes.length; i++) {
			result[i * 2] = digital[(bytes[i] & 0xf0) >> 4];
			result[i * 2 + 1] = digital[bytes[i] & 0x0f];
		}
		return new String(result);
	}

	/*
	 * 加签算法
	 * */
	public static <T> String doHmacSHA2(String path, Map<String, T> params, String key, String timestamp) {
		
		List<Map.Entry<String, T>> parameters = new ArrayList<Map.Entry<String,T>>(params.entrySet());
		SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA256");
		Charset CHARSET_UTF8 = Charset.forName("UTF-8");
		Mac mac;
		try {
			mac = Mac.getInstance("HmacSHA256");
			mac.init(signingKey);
		} catch (NoSuchAlgorithmException e) {
			throw new IllegalStateException(e.getMessage(), e);
		} catch (InvalidKeyException e) {
			throw new IllegalStateException(e.getMessage(), e);
		}
		if(path != null && path.length() > 0){
			mac.update(path.getBytes(CHARSET_UTF8));
		}
		if(parameters != null){
			Collections.sort(parameters, new MapEntryComparator<String, T>());
			for (Map.Entry<String, T> parameter : parameters) {
				byte[] name = parameter.getKey().getBytes(CHARSET_UTF8);
				Object value = parameter.getValue();
				if(value instanceof Collection){
					for (Object o : (Collection)value){
						mac.update(name);
						if(o != null){
							mac.update(o.toString().getBytes(CHARSET_UTF8));
						}
					}
				}else{
					mac.update(name);
					if(value != null){
						mac.update(value.toString().getBytes(CHARSET_UTF8));
					}
				}
			}
		}
		if(timestamp != null && timestamp.length() > 0){
			mac.update(timestamp.toString().getBytes(CHARSET_UTF8));
		}
		return encodeHexStr(mac.doFinal());
	}
}

/*
 * Map参数排序类
 * */

class MapEntryComparator<K, V> implements Comparator<Map.Entry<K, V>> {

    @Override
    public int compare(Entry<K, V> o1, Entry<K, V> o2) {
        if (o1 == o2) {
            return 0;
        }
        final String k1 = o1.getKey().toString();
        final String k2 = o2.getKey().toString();
        int l1 = k1.length();
        int l2a = k2.length();
        for (int i = 0; i < l1; i++) {
            char c1 = k1.charAt(i);
            char c2;
            if (i < l2a) {
                c2 = k2.charAt(i);
            } else {
                return 1;
            }
            if (c1 > c2) {
                return 1;
            } else if (c1 < c2) {
                return -1;
            }
        }
        if (l1 < l2a) {
          return -1;
        }else if(l1 == l2a) {
          return 0;
        }else {
          return -1;
        }
    }
}