OpenApi协议文档设计完整流程(含h5与服务器调用完整案例)

API调用方法详解

API是基于HTTP协议来调用的,开发者 可以根据的协议来封装HTTP请求进行调用。(参考淘宝API 写的)

调用流程

协议:填充参数 > 生成签名 > 拼装HTTP请求 > 发起HTTP请求> 得到HTTP响应 > 解释json结果,以下是大体的调用过程示意图:

在这里插入图片描述

调用入口

调用API的服务URL地址

公共参数

调用任何一个API都必须传入的参数,目前支持的公共参数有 :

参数名称 参数类型 是否必须 参数描述
method String API接口名称。
app_key String TOP分配给应用的AppKey。
timestamp String 时间戳,格式为yyyy-MM-dd HH:mm:ss,时区为GMT+8,例如:2016-01-01 12:00:00。服务端允许客户端请求最大时间误差为10分钟。
format String 响应格式。默认为xml格式,可选值:xml,json。
v String API协议版本,可选值:2.0。
sign_method String 签名的摘要算法,可选值为:hmac,md5。
sign String API输入参数签名结果,签名算法参照下面的介绍。

业务参数

API调用除了必须包含公共参数外,如果API本身有业务级的参数也必须传入,每个API的业务级参数请考API文档说明。

签名算法

为了防止API调用过程中被黑客恶意篡改,调用任何一个API都需要携带签名,服务端会根据请求参数,对签名进行验证,签名不合法的请求将会被拒绝。目前支持的签名算法有两种:MD5(sign_method=md5),HMAC_MD5(sign_method=hmac),签名大体过程如下:

  • 对所有API请求参数(包括公共参数和业务参数,但除去sign参数和byte[]类型的参数),根据参数名称的ASCII码表的顺序排序。如:foo:1, bar:2, foo_bar:3, foobar:4排序后的顺序是bar:2, foo:1, foo_bar:3, foobar:4。
  • 将排序好的参数名和参数值拼装在一起,根据上面的示例得到的结果为:bar2foo1foo_bar3foobar4。
  • 把拼装好的字符串采用utf-8编码,使用签名算法对编码后的字节流进行摘要。如果使用MD5算法,则需要在拼装的字符串前后加上app的secret后,再进行摘要,如:md5(secret+bar2foo1foo_bar3foobar4+secret);如果使用HMAC_MD5算法,则需要用app的secret初始化摘要算法后,再进行摘要,如:hmac_md5(bar2foo1foo_bar3foobar4)。
  • 将摘要得到的字节流结果使用十六进制表示,如:hex(“helloworld”.getBytes(“utf-8”)) = “68656C6C6F776F726C64”

说明:MD5和HMAC_MD5都是128位长度的摘要算法,用16进制表示,一个十六进制的字符能表示4个位,所以签名后的字符串长度固定为32个十六进制字符。

Java签名示例代码

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Map;

/**
 * 

Description

*

* ====================== * ====================== * * @Author created by lgy * @Date 2019/9/19 */
public class SignMethodParamsRequest { private static final String SIGN_METHOD_MD5 = "md5"; private static final String SIGN_METHOD_HMAC = "hmac"; private static final String CHARSET_UTF8 = "utf-8"; /** *

方法参数签名

* ======================== * * @param params * @param secret * @param signMethod * @return * @throws IOException */
public static String signParamsRequest(Map<String, String> params, String secret, String signMethod) throws IOException { // 第一步:检查参数是否已经排序 String[] keys = params.keySet().toArray(new String[0]); Arrays.sort(keys); // 第二步:把所有参数名和参数值串在一起 StringBuilder query = new StringBuilder(); if (SIGN_METHOD_MD5.equals(signMethod)) { query.append(secret); } for (String key : keys) { String value = params.get(key); if (isNotEmpty(key) && isNotEmpty(value)) { query.append(key).append(value); } } // 第三步:使用MD5/HMAC加密 byte[] bytes; if (SIGN_METHOD_HMAC.equals(signMethod)) { bytes = encryptHMAC(query.toString(), secret); } else { query.append(secret); System.out.println("字符串为:" + query); bytes = encryptMD5(query.toString()); } // 第四步:把二进制转化为大写的十六进制 return byte2hex(bytes); } /** *

对字节流进行HMAC_MD5摘要

* ======================== * * @param data * @param secret * @return * @throws IOException */
private static byte[] encryptHMAC(String data, String secret) throws IOException { byte[] bytes = null; try { SecretKey secretKey = new SecretKeySpec(secret.getBytes(CHARSET_UTF8), "HmacMD5"); Mac mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKey); bytes = mac.doFinal(data.getBytes(CHARSET_UTF8)); } catch (GeneralSecurityException gse) { throw new IOException(gse.toString()); } return bytes; } /** *

对字符串采用UTF-8编码后,用MD5进行摘要

* ======================== * * @param data * @return * @throws IOException */
private static byte[] encryptMD5(String data) throws IOException { return encryptMD5(data.getBytes(CHARSET_UTF8)); } /** *

对字节流进行MD5摘要。

* ======================== * * @param data * @return * @throws IOException */
private static byte[] encryptMD5(byte[] data) throws IOException { byte[] bytes = null; try { MessageDigest md = MessageDigest.getInstance("MD5"); bytes = md.digest(data); } catch (GeneralSecurityException gse) { throw new IOException(gse.toString()); } return bytes; } /** *

把字节流转换为十六进制表示方式。

* ======================== * * @param bytes * @return */
private static String byte2hex(byte[] bytes) { StringBuilder sign = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(bytes[i] & 0xFF); if (hex.length() == 1) { sign.append("0"); } sign.append(hex.toUpperCase()); } return sign.toString(); } /** *

非空判断

* ======================== * * @param value * @return */
private static boolean isNotEmpty(String value) { int strLen; if (value == null || (strLen = value.length()) == 0) { return false; } for (int i = 0; i < strLen; i++) { if ((Character.isWhitespace(value.charAt(i)) == false)) { return true; } } return false; } }

web调用示例

medgest-md5.js

function array(n) {
	for (i = 0; i < n; i++) this[i] = 0;
	this.length = n;
}
/* Quelques fonctions fondamentales doivent ¨ºtre transform¨¦es ¨¤ cause
 * d'erreurs Javascript.
 * Essayez par exemple de calculer 0xffffffff >> 4 ...
 * Les fonctions utilis¨¦es maintenant sont il est vrai plus lentes que les
 * fonctions originales mais elles fonctionnent.
 */

function integer(n) {
	return n % (0xffffffff + 1);
}

function shr(a, b) {
	a = integer(a);
	b = integer(b);
	if (a - 0x80000000 >= 0) {
		a = a % 0x80000000;
		a >>= b;
		a += 0x40000000 >> (b - 1);
	} else
		a >>= b;
	return a;
}

function shl1(a) {
	a = a % 0x80000000;
	if (a & 0x40000000 == 0x40000000) {
		a -= 0x40000000;
		a *= 2;
		a += 0x80000000;
	} else
		a *= 2;
	return a;
}

function shl(a, b) {
	a = integer(a);
	b = integer(b);
	for (var i = 0; i < b; i++) a = shl1(a);
	return a;
}

function and(a, b) {
	a = integer(a);
	b = integer(b);
	var t1 = (a - 0x80000000);
	var t2 = (b - 0x80000000);
	if (t1 >= 0)
		if (t2 >= 0)
			return ((t1 & t2) + 0x80000000);
		else
			return (t1 & b);
	else
	if (t2 >= 0)
		return (a & t2);
	else
		return (a & b);
}

function or(a, b) {
	a = integer(a);
	b = integer(b);
	var t1 = (a - 0x80000000);
	var t2 = (b - 0x80000000);
	if (t1 >= 0)
		if (t2 >= 0)
			return ((t1 | t2) + 0x80000000);
		else
			return ((t1 | b) + 0x80000000);
	else
	if (t2 >= 0)
		return ((a | t2) + 0x80000000);
	else
		return (a | b);
}

function xor(a, b) {
	a = integer(a);
	b = integer(b);
	var t1 = (a - 0x80000000);
	var t2 = (b - 0x80000000);
	if (t1 >= 0)
		if (t2 >= 0)
			return (t1 ^ t2);
		else
			return ((t1 ^ b) + 0x80000000);
	else
	if (t2 >= 0)
		return ((a ^ t2) + 0x80000000);
	else
		return (a ^ b);
}

function not(a) {
	a = integer(a);
	return (0xffffffff - a);
}

/* D¨¦but de l'algorithme */

var state = new array(4);
var count = new array(2);
count[0] = 0;
count[1] = 0;
var buffer = new array(64);
var transformBuffer = new array(16);
var digestBits = new array(16);

var S11 = 7;
var S12 = 12;
var S13 = 17;
var S14 = 22;
var S21 = 5;
var S22 = 9;
var S23 = 14;
var S24 = 20;
var S31 = 4;
var S32 = 11;
var S33 = 16;
var S34 = 23;
var S41 = 6;
var S42 = 10;
var S43 = 15;
var S44 = 21;

function F(x, y, z) {
	return or(and(x, y), and(not(x), z));
}

function G(x, y, z) {
	return or(and(x, z), and(y, not(z)));
}

function H(x, y, z) {
	return xor(xor(x, y), z);
}

function I(x, y, z) {
	return xor(y, or(x, not(z)));
}

function rotateLeft(a, n) {
	return or(shl(a, n), (shr(a, (32 - n))));
}

function FF(a, b, c, d, x, s, ac) {
	a = a + F(b, c, d) + x + ac;
	a = rotateLeft(a, s);
	a = a + b;
	return a;
}

function GG(a, b, c, d, x, s, ac) {
	a = a + G(b, c, d) + x + ac;
	a = rotateLeft(a, s);
	a = a + b;
	return a;
}

function HH(a, b, c, d, x, s, ac) {
	a = a + H(b, c, d) + x + ac;
	a = rotateLeft(a, s);
	a = a + b;
	return a;
}

function II(a, b, c, d, x, s, ac) {
	a = a + I(b, c, d) + x + ac;
	a = rotateLeft(a, s);
	a = a + b;
	return a;
}

function transform(buf, offset) {
	var a = 0,
		b = 0,
		c = 0,
		d = 0;
	var x = transformBuffer;

	a = state[0];
	b = state[1];
	c = state[2];
	d = state[3];

	for (i = 0; i < 16; i++) {
		x[i] = and(buf[i * 4 + offset], 0xff);
		for (j = 1; j < 4; j++) {
			x[i] += shl(and(buf[i * 4 + j + offset], 0xff), j * 8);
		}
	}
	/* tour 1 */
	a = FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
	d = FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
	c = FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
	b = FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
	a = FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
	d = FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
	c = FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
	b = FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
	a = FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
	d = FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
	c = FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
	b = FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
	a = FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
	d = FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
	c = FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
	b = FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
	/* tour 2 */
	a = GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
	d = GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
	c = GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
	b = GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
	a = GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
	d = GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
	c = GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
	b = GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
	a = GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
	d = GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
	c = GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
	b = GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
	a = GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
	d = GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
	c = GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
	b = GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
	/* tour 3 */
	a = HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
	d = HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
	c = HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
	b = HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
	a = HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
	d = HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
	c = HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
	b = HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
	a = HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
	d = HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
	c = HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
	b = HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
	a = HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
	d = HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
	c = HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
	b = HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
	/* tour 4 */
	a = II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
	d = II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
	c = II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
	b = II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
	a = II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
	d = II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
	c = II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
	b = II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
	a = II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
	d = II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
	c = II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
	b = II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
	a = II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
	d = II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
	c = II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
	b = II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
	state[0] += a;
	state[1] += b;
	state[2] += c;
	state[3] += d;
}
/* Avec l'initialisation de  Dobbertin:
       state[0] = 0x12ac2375;
       state[1] = 0x3b341042;
       state[2] = 0x5f62b97c;
       state[3] = 0x4ba763ed;
       s'il y a une collision:
       begin 644 Message1
       M7MH=JO6_>MG!X?!51$)W,CXV!A"=(!AR71,TF$W()/MEHR%C4:G1R:Q"=
       `
       end
 
       begin 644 Message2
       M7MH=JO6_>MG!X?!51$)W,CXV!A"=(!AR71,TF$W()/MEHREC4:G1R:Q"=
       `
       end
    */
function init() {
	count[0] = count[1] = 0;
	state[0] = 0x67452301;
	state[1] = 0xefcdab89;
	state[2] = 0x98badcfe;
	state[3] = 0x10325476;
	for (i = 0; i < digestBits.length; i++)
		digestBits[i] = 0;
}

function update(b) {
	var index, i;

	index = and(shr(count[0], 3), 0x3f);
	if (count[0] < 0xffffffff - 7)
		count[0] += 8;
	else {
		count[1]++;
		count[0] -= 0xffffffff + 1;
		count[0] += 8;
	}
	buffer[index] = and(b, 0xff);
	if (index >= 63) {
		transform(buffer, 0);
	}
}

function finish() {
	var bits = new array(8);
	var padding;
	var i = 0,
		index = 0,
		padLen = 0;

	for (i = 0; i < 4; i++) {
		bits[i] = and(shr(count[0], (i * 8)), 0xff);
	}
	for (i = 0; i < 4; i++) {
		bits[i + 4] = and(shr(count[1], (i * 8)), 0xff);
	}
	index = and(shr(count[0], 3), 0x3f);
	padLen = (index < 56) ? (56 - index) : (120 - index);
	padding = new array(64);
	padding[0] = 0x80;
	for (i = 0; i < padLen; i++)
		update(padding[i]);
	for (i = 0; i < 8; i++)
		update(bits[i]);
	for (i = 0; i < 4; i++) {
		for (j = 0; j < 4; j++) {
			digestBits[i * 4 + j] = and(shr(state[i], (j * 8)), 0xff);
		}
	}
}
/* Fin de l'algorithme MD5 */
function hexa(n) {
	var hexa_h = "0123456789abcdef";
	var hexa_c = "";
	var hexa_m = n;
	for (hexa_i = 0; hexa_i < 8; hexa_i++) {
		hexa_c = hexa_h.charAt(Math.abs(hexa_m) % 16) + hexa_c;
		hexa_m = Math.floor(hexa_m / 16);
	}
	return hexa_c;
}
var ascii = "01234567890123456789012345678901" +
	" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
	"[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";

function MD5(message) {
	var l, s, k, ka, kb, kc, kd;

	init();
	for (k = 0; k < message.length; k++) {
		l = message.charAt(k);
		update(ascii.lastIndexOf(l));
	}
	finish();
	ka = kb = kc = kd = 0;
	for (i = 0; i < 4; i++) ka += shl(digestBits[15 - i], (i * 8));
	for (i = 4; i < 8; i++) kb += shl(digestBits[15 - i], ((i - 4) * 8));
	for (i = 8; i < 12; i++) kc += shl(digestBits[15 - i], ((i - 8) * 8));
	for (i = 12; i < 16; i++) kd += shl(digestBits[15 - i], ((i - 12) * 8));
	s = hexa(kd) + hexa(kc) + hexa(kb) + hexa(ka);
	return s;
}

sign-params.js

// 业务参数排序
function serviceParamsSort(serviceParams) {
	return serviceParams.sort().join("");
}

//获取当前时间 时间戳yyyy-MM-dd HH:mm:ss 格式
function getFormatDate() {
	var date = new Date();
	var seperator1 = "-";
	var seperator2 = ":";
	var month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;
	var strDate = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
	var currentdate = date.getFullYear() + seperator1 + month + seperator1 + strDate +
		" " + date.getHours() + seperator2 + date.getMinutes() +
		seperator2 + date.getSeconds();
	return currentdate;
}

// 格式化时间戳 yyyy-MM-dd HH:mm:ss
function formatDate(date) {
	var seperator1 = "-";
	var seperator2 = ":";
	var month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1;
	var strDate = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
	var currentdate = date.getFullYear() + seperator1 + month + seperator1 + strDate +
		" " + date.getHours() + seperator2 + date.getMinutes() +
		seperator2 + date.getSeconds();
	return currentdate;
}

//方法参数验签
function signParam(maps, app_secret, sign_method) {
	var keys = new Array();
	for (var key of maps.keys()) {
		keys.push(key);
	}
	//第一步   先对键值排序
	keys = keys.sort();

	// 第二部  将所有的参数名和参数值拼接在一起
	var query = "";
	if (sign_method == "md5") {
		query = query + app_secret;
	}
	// 拼接maps 的key 和value值
	for (var key of keys) {
		var value = maps.get(key);
		if ((key != null && key != "") && (value != null && value != "")) {
			query = query + key + value;
		}
	}
	// 第三部  使用md5加密
	var bytes = new Array();
	if (sign_method == "md5") {
		query = query + app_secret;
		return MD5(query).toUpperCase();
	}
}

function stringToBytes(str) {
	var ch, st, re = [];
	for (var i = 0; i < str.length; i++) {
		ch = str.charCodeAt(i); // get char   
		st = []; // set up "stack"  
		do {
			st.push(ch & 0xFF); // push byte to stack  
			ch = ch >> 8; // shift value down by 1 byte  
		}
		while (ch);
		// add stack contents to result  
		// done because chars have "wrong" endianness  
		re = re.concat(st.reverse());
	}
	// return an array of bytes  
	return re;
}

服务器过滤器(较早版本)

/** ----解决跨域访问报错---- */
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST,GET,PUT,OPTIONS,DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers",
                "Origin,X-Requested-With,Content-Type,Accept,Authorization,token");
        /** ----解决跨域访问报错---- */
        /** -------------- open api校验  ./start-------------- */
        String appkey = request.getParameter("app_key");
        String v = request.getParameter("v");
        String timestamp = request.getParameter("timestamp");
        String signMethod = request.getParameter("sign_method");
        String format = request.getParameter("format");
        String clientSign = request.getParameter("sign");
        String msg = validParamsNull(appkey, v, timestamp, signMethod, format, clientSign);
        if (msg.length() > 2) {
            RenderUtil.renderJson(response, new ErrorTip(700, msg));
            return;
        }
        /** -------------- open api校验  ./end-------------- */
        /** ---------- 私钥公钥版本校验 ----------------*/
        String appSecret = "";
        Map map = new HashMap();
        map.put("app_key", appkey);
        map.put("v", v);
        TOpenApi openApi = this.openApiService.getOpenApiByAppkKeyV(appkey, v);
        // 非授权公钥 或公钥与版本不一致
        if (openApi == null) {
            RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.APP_KEY_ILLEGAL.getCode(),
                    BizExceptionEnum.APP_KEY_ILLEGAL.getMessage()));
            return;
        } else {
            appSecret = openApi.getAppSecret();
        }
        // 时间校验,允许10分钟上下的时间差
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = null;
        boolean result = true;
        try {
            date = df.parse(timestamp);
            Date serverDate = new Date();
            long clientDateTime = date.getTime();
            long serverDateTime = serverDate.getTime();
            long disparity = Math.abs(serverDateTime - clientDateTime);
            // 时间差为大小10分钟之内,result 改为true
            if (disparity <= 600000) {
                result = false;
            }
        } catch (ParseException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        // 时间差超过10分种
        if (result) {
            RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TIMESTAMP_DIFFERENCE_BIG.getCode(),
                    BizExceptionEnum.TIMESTAMP_DIFFERENCE_BIG.getMessage()));
            return;
        }
        // 参数签名校验
        Map maps = new HashMap();
        Enumeration paramsNames = request.getParameterNames();
        String[] ex = {"app_key", "timestamp", "v", "format", "sign", "app_secret", "sign_method"};
        String params = "";
        for (Enumeration e = paramsNames; e.hasMoreElements(); ) {
            String paramName = e.nextElement().toString();
            params = params + paramName + ",";
        }
        // 去除ex包含的公共参数
        Set set = new HashSet(Arrays.asList(params.split(",")));
        for (String e : ex) {
            set.remove(e);
        }
        params = StringUtils.join(set.toArray(), ",");
        // 公共参数
        maps.put("method", "zhms.item.seller.get");
        maps.put("app_key", appkey);
        maps.put("format", "json");
        maps.put("v", "1.0");
        maps.put("sign_method", signMethod);
        maps.put("timestamp", df.format(date));
        maps.put("app_secret", appSecret);
        // 业务参数
        String[] paramsObj = params.split(",");
        Arrays.sort(paramsObj);
        params = "";
        for (String str : paramsObj) {
            params = params + str;
        }
        maps.put("fields", params);
        logger.info("参数校验:" + maps);
        // 比对参数签名是否一致,不一致校验不通过,无权调用api
        String ServerSign = SignMethodParamsRequest.signParamsRequest(maps, appSecret, signMethod);
        System.out.println(ServerSign);
        if (!clientSign.equals(ServerSign)) {
            RenderUtil.renderJson(response,
                    new ErrorTip(BizExceptionEnum.SIGN_ERROR.getCode(), BizExceptionEnum.SIGN_ERROR.getMessage()));
            return;
        }

其他语言签名示例代码请自行解决。

调用示例

1. 设置参数值

公共参数:

  • method = “bingo.item.seller.get”
  • app_key = “12345678”
  • session = “test”
  • timestamp = “2016-01-01 12:00:00” (与服务器时间差不得超过15分钟)
  • format = “json”
  • v = “2.0”
  • sign_method = “md5”

业务参数:

  • fields = “num_iid,title,nick,price,num”
  • num_iid = 11223344

2. 按ASCII顺序排序

  • app_key = “12345678”
  • fields = “num_iid,title,nick,price,num”
  • format = “json”
  • method = “bingo.item.seller.get”
  • num_iid = 11223344
  • session = “test”
  • sign_method = “md5”
  • timestamp = “2016-01-01 12:00:00”
  • v = “2.0”

3. 拼接参数名与参数值
a p p k e y 12345678 f i e l d s n u m i i d , t i t l e , n i c k , p r i c e , n u m f o r m a t j s o n m e t h o d t a o b a o . i t e m . s e l l e r . g e t n u m i i d 11223344 s e s s i o n t e s t s i g n m e t h o d m d 5 t i m e s t a m p 2016 − 01 − 0112 : 00 : 00 v 2.0 app_key12345678fieldsnum_iid,title,nick,price,numformatjsonmethodtaobao.item.seller.getnum_iid11223344sessiontestsign_methodmd5timestamp2016-01-01 12:00:00v2.0 appkey12345678fieldsnumiid,title,nick,price,numformatjsonmethodtaobao.item.seller.getnumiid11223344sessiontestsignmethodmd5timestamp2016010112:00:00v2.0
4. 生成签名
假设app的secret为helloworld,则签名结果为:hex(md5(helloworld+按顺序拼接好的参数名与参数值+helloworld)) = “66987CB115214E59E6EC978214934FB8”

5. 组装HTTP请求
将所有参数名和参数值采用utf-8进行URL编码(参数顺序可随意,但必须要包括签名参数),然后通过GET或POST(含byte[]类型参数)发起请求,如:

http://www.bingo.com/router/rest?method=bingo.item.seller.get&app_key=12345678&session=test×tamp=2016-01-01+12%3A00%3A00&format=json&v=2.0&sign_method=md5&fields=num_iid%2Ctitle%2Cnick%2Cprice%2Cnum&num_iid=11223344&sign=66987CB115214E59E6EC978214934FB8

注意事项

  • 所有的请求和响应数据编码皆为utf-8格式,URL里的所有参数名和参数值请做URL编码。如果请求的Content-Type是application/x-www-form-urlencoded,则HTTP Body体里的所有参数值也做URL编码;如果是multipart/form-data格式,每个表单字段的参数值无需编码,但每个表单字段的charset部分需要指定为utf-8。
  • 参数名与参数值拼装起来的URL长度小于1024个字符时,可以用GET发起请求;参数类型含byte[]类型或拼装好的请求URL过长时,必须用POST发起请求。所有API都可以用POST发起请求。

你可能感兴趣的:(OpenApi)