在與公司外部系統對接時,API接口一般采用REST風格,對外暴露HTTP服務。只需要將入參封裝好,並發起HTTP請求即可。具體請求流程如下圖所示:
數據格式
API調用參數分為系統參數和業務參數,請求時,系統參數是必傳的,否則無法成功請求,業務參數由具體業務接口定義。
系統參數 | |||
---|---|---|---|
名稱 | 類型 | 必填 | 描述 |
apiKey | String | 是 | 分配給供應商的唯一身份標識 |
sign | String | 是 | 請求簽名,生成規則參見簽名機制 |
timestamp | String | 是 | 時間戳,參見時間戳 |
API請求返回結果目前支持json和xml格式,具體返回格式由請求頭中的Content-Type屬性來決定。當Content-Type屬性為application/xml時,返回xml格式,其余情況下統一返回json格式。返回結果包含以下字段
系統參數 | |||
---|---|---|---|
名稱 | 類型 | 必填 | 描述 |
returnCode | int | 是 | 結果碼,具體值參見API返回碼 |
errorMsg | String | 否 | 異常時錯誤信息 |
data | String | 否 | 返回結果 |
success | boolean | 是 | 是否請求成功 |
請求示例
我們以團期信息管理接口為例,假設我們需要維護團期,接口請求原始入參(業務參數+系統參數)如下.
{
"apiKey": "testApiKey",//系統參數
"timestamp": "2015-07-30 12:34:56",//系統參數
"agencyProductId": "test10001",//業務參數
"groupNum": "",//業務參數
"planInfo": [//業務參數
{
"planDateStr": "2015-07-18",
"datePriceList": [
{
"schemeId": "scheme0001",
"scheduleId": "schedule",
"agencyBudget": 1000,
"agencyBudgetChild": 500,
"excludeChild": 1,
"roomAddBudget": 100,
"roomGapFlag": 1,
"aheaddate": 4,
"deadlinedate": 3,
"deadlinehour": 18,
"promoFlag": 1,
"setGroupFlag": 1,
"stuffEndDate": 5
}
]
}
]
}
首先,我們需要根據現有參數生成簽名,簽名生成步驟如下:
1. 將入參按照一級key值進行排序(按字典順序進行排序,忽略大小寫),去掉value值為空的入參,我們將得到以下格式參數(業務參數+系統參數)如下
{
"agencyProductId": "test10001",
"apiKey": "testApiKey",
"planInfo": [
{
"planDateStr": "2015-07-18",
"datePriceList": [
{
"schemeId": "scheme0001",
"scheduleId": "schedule",
"agencyBudget": 1000,
"agencyBudgetChild": 500,
"excludeChild": 1,
"roomAddBudget": 100,
"roomGapFlag": 1,
"aheaddate": 4,
"deadlinedate": 3,
"deadlinehour": 18,
"promoFlag": 1,
"setGroupFlag": 1,
"stuffEndDate": 5
}
]
}
],
"timestamp": "2015-07-30 12:34:56"
}
2. 獲取一級key和對應的value,例如上面參數中planInfo鍵,對應的值為[{"planDateStr":"2015-07-18","datePriceList":[{"schemeId":"scheme0001","scheduleId":"schedule","agencyBudget":1000,"agencyBudgetChild":500,"excludeChild":1,"roomAddBudget":100,"roomGapFlag":1,"aheaddate":4,"deadlinedate":3,"deadlinehour":18,"promoFlag":1,"setGroupFlag":1,"stuffEndDate":5}]}], 以字符串的形式把key+value拼接起來得到一個新的字符串(二級三級鍵不進行操作),如下:
planInfo[{"planDateStr":"2015-07-18","datePriceList":[{"schemeId":"scheme0001","scheduleId":"schedule","agencyBudget":1000,
"agencyBudgetChild":500,"excludeChild":1,"roomAddBudget":100,"roomGapFlag":1,"aheaddate":4,"deadlinedate":3,"deadlinehour":18,
"promoFlag":1,"setGroupFlag":1,"stuffEndDate":5}]}]
其他鍵值對同樣如此,將拼裝好的字符串再依次拼接起來,如下:
agencyProductIdtest10001apiKeytestApiKeyplanInfo[{"planDateStr":"2015-07-18","datePriceList":[{"schemeId":"scheme0001","scheduleId":"schedule",
"agencyBudget":1000,"agencyBudgetChild":500,"excludeChild":1,"roomAddBudget":100,"roomGapFlag":1,"aheaddate":4,"deadlinedate":3,"deadlinehour":18,
"promoFlag":1,"setGroupFlag":1,"stuffEndDate":5}]}]timestamp2015-07-30 12:34:56
3. 在拼好的字符串前后都加上簽名密鑰,我們假設密鑰是ZbWjUMYevqT9Tnup4jRs,可以得到以下字符串:
ZbWjUMYevqT9Tnup4jRsagencyProductIdtest10001apiKeytestApiKeyplanInfo[{"planDateStr":"2015-07-18","datePriceList":[{"schemeId":"scheme0001",
"scheduleId":"schedule","agencyBudget":1000,"agencyBudgetChild":500,"excludeChild":1,"roomAddBudget":100,"roomGapFlag":1,"aheaddate":4,
"deadlinedate":3,"deadlinehour":18,"promoFlag":1,"setGroupFlag":1,"stuffEndDate":5}]}]timestamp2015-07-30 12:34:56ZbWjUMYevqT9Tnup4jRs
4. 對生成的字符串進行MD5加密,並將結果全部轉為大寫,獲得簽名值:
85F60EFE28BB4688F3BA4A37FF62C101
5. 將簽名加入到入參中:
{
"agencyProductId": "test10001",
"apiKey": "testApiKey",
"planInfo": [
{
"planDateStr": "2015-07-18",
"datePriceList": [
{
"schemeId": "scheme0001",
"scheduleId": "schedule",
"agencyBudget": 1000,
"agencyBudgetChild": 500,
"excludeChild": 1,
"roomAddBudget": 100,
"roomGapFlag": 1,
"aheaddate": 4,
"deadlinedate": 3,
"deadlinehour": 18,
"promoFlag": 1,
"setGroupFlag": 1,
"stuffEndDate": 5
}
]
}
],
"timestamp": "2015-07-30 12:34:56",
"sign": "85F60EFE28BB4688F3BA4A37FF62C101"
}
6.發起HTTP請求。
附上MD5加密方法參考示例:
private static String Md5Encode(String str) throws NoSuchAlgorithmException { StringBuilder sign = new StringBuilder(); MessageDigest md = MessageDigest.getInstance("MD5"); byte[] bytes = md.digest(str.getBytes()); 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(); }
在spring-core包中提供了一個MD5加密工具!
org.springframework.util.DigestUtils#md5DigestAsHex(byte[])
注意事項
1. 所有的請求和響應數據編碼皆為utf-8格式
2. 生成簽名時,空值的參數不參與校驗
3. 生成簽名時,參數名稱和值大小寫敏感
4. 排序規則為按字典順序進行排序,忽略大小寫
附代碼實現:
public class APITest { public static void main(String[] args) throws NoSuchAlgorithmException { String s = "{\n" +
" \"apiKey\": \"testApiKey\",\n" +
" \"timestamp\": \"2015-07-30 12:34:56\",\n" +
" \"agencyProductId\": \"test10001\",\n" +
" \"groupNum\": \"\",\n" +
" \"planInfo\": [\n" +
" {\n" +
" \"planDateStr\": \"2015-07-18\",\n" +
" \"datePriceList\": [\n" +
" {\n" +
" \"schemeId\": \"scheme0001\",\n" +
" \"scheduleId\": \"schedule\",\n" +
" \"agencyBudget\": 1000,\n" +
" \"agencyBudgetChild\": 500,\n" +
" \"excludeChild\": 1,\n" +
" \"roomAddBudget\": 100,\n" +
" \"roomGapFlag\": 1,\n" +
" \"aheaddate\": 4,\n" +
" \"deadlinedate\": 3,\n" +
" \"deadlinehour\": 18,\n" +
" \"promoFlag\": 1,\n" +
" \"setGroupFlag\": 1,\n" +
" \"stuffEndDate\": 5\n" +
" }\n" +
" ]\n" +
" }\n" +
" ]\n" +
"}"; String signature = getSignature(s, "ZbWjUMYevqT9Tnup4jRs"); System.out.println(Md5Encode(signature)); } public static String getSignature(String requestData, String secretKey) { //第一步,獲取所有值非空的key
List<String> keyList = new ArrayList<String>(); Map<String,Object> data = JsonUtil.toBean(requestData,Map.class); for (String key : data.keySet()) { if (key == null) { continue; } //value為null或空
if (data.get(key) == null || StringUtils.isBlank(data.get(key).toString())) { continue; } keyList.add(key); } //按名稱排序並拼接成字符串
String[] arrayToSort = keyList.toArray(new String[keyList.size()]); Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER); StringBuilder sb = new StringBuilder(secretKey); for (String key : arrayToSort) { sb.append(key); sb.append(JsonUtil.toString(data.get(key))); } sb.append(secretKey); System.out.println(sb.toString()); return sb.toString(); } /** * MD5 加密 * @param str 需要加密的字符串 * @return 經過加密的字符串 * @throws NoSuchAlgorithmException */
private static String Md5Encode(String str) throws NoSuchAlgorithmException { StringBuilder sign = new StringBuilder(); MessageDigest md = MessageDigest.getInstance("MD5"); byte[] bytes = md.digest(str.getBytes()); 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(); } }