前提说明
1、2.1.40及以上版本的techWrapper服务一键部署包支持通过签名验签方式进行HTTP请求。
2、加签规则:
将请求body体的JSON字符串作为待签名明文信息,并用projectSecret作为密钥通过哈希算法进行请求签名值的计算。
部署说明
techWrapper服务一键部署包使用说明
参考链接:https://qianxiaoxia.yuque.com/docs/share/7c96dfbc-1e53-4de3-b1f6-46e10eda67b4
签名验签服务开启说明
注意事项:
1、签名验签服务为可选服务,如果开发者无需签名验签方式进行HTTP请求,只需按照techWrapper服务一键部署说明配置后,即可启动项目。
2、如果开发者需要通过签名验签方式进行HTTP请求,需接入2.1.40及以上版本的techWrapper服务包,并按照以下说明开启签名验签服务。
techWrapper服务包部署完成后,在启动服务之前,先开启签名验签服务配置。
1、找到文件,目录结构如下图所示:
2、编辑文件,开启签名验签服务。
首次打开文件后,默认配置项展示图如下:
开启签名验签服务后,配置项展示图如下:
3、以上配置完成后,保存文件,并按照techWrapper部署说明启动服务。
请求头说明
开发者如果使用签名验签方式进行HTTP请求时,需要在请求过程中,添加请求头。
请求头格式如下:
参数名称 | 必选 | 参数说明 | 示例值 |
X-timevale-signature | 是 | 请求签名值 | |
Content-Type | 否 | 固定类型,application/json |
请求签名算法
1、JS示例
//请求数据data var data = JSON.stringify(request.data); data = data.substr(1, data.length - 2).replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\r/g, "\r").replace(/\\"/g, "\""); //获取签名值 var signature = CryptoJS.HmacSHA256(data, "密钥").toString();
2、postman工具,使用Pre-request Script实现签名算法
//请求数据data var data = JSON.stringify(request.data); data = data.substr(1, data.length - 2).replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\r/g, "\r").replace(/\\"/g, "\""); //获取签名值 var signature = CryptoJS.HmacSHA256(data, postman.getEnvironmentVariable("projectSecret")).toString(); postman.setEnvironmentVariable("signature", signature)
3、JAVA开发语言示例
public static void main(String[] args) { JSONObject params = new JSONObject(); params.put("name","宗炼"); params.put("idNo","34***15"); params.put("mobile","173***6"); params.put("title","职位"); params.put("personArea","19"); // 请求签名值 String signature = getSignature(params.toString(), "项目id对应的密钥projectsecret","HmacSHA256","UTF-8"); System.out.println(params.toString()); System.out.println("请求签名值 = " + signature); } /*** * 获取请求签名值 * * @param data 加密前数据 * @param key 密钥 * @param algorithm HmacMD5 HmacSHA1 HmacSHA256 HmacSHA384 HmacSHA512 * @param encoding 编码格式 * @return HMAC加密后16进制字符串 * @throws Exception */ public static String getSignature(String data, String key, String algorithm, String encoding) { Mac mac = null; try { mac = Mac.getInstance(algorithm); SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(encoding), algorithm); mac.init(secretKey); mac.update(data.getBytes(encoding)); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return null; } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } catch (InvalidKeyException e) { e.printStackTrace(); return null; } return byte2hex(mac.doFinal()); } /*** * 将byte[]转成16进制字符串 * * @param data * * @return 16进制字符串 */ public static String byte2hex(byte[] data) { StringBuilder hash = new StringBuilder(); String stmp; for (int n = 0; data != null && n < data.length; n++) { stmp = Integer.toHexString(data[n] & 0XFF); if (stmp.length() == 1) hash.append('0'); hash.append(stmp); } return hash.toString(); }
注:如果开发者使用文件流方式进行签署请求时,可参考下面提供的代码示例进行请求
(以平台自身摘要签署-文件流方式请求为例)
文件流方式进行签署请求时,注意事项:
1、请求body体可以直接在入参中就规定好顺序,例如入参参数顺序:file,sealId,signPos,signType,或者入参定义完成之后,按照字母顺序重新进行排序
2、转换入参信息,将常规map的入参转为url参数
public static void main(String[] args) { String url = "http://localhost:8080/tech-sdkwrapper/timevale/sign/selfStreamSign"; JSONObject params = new JSONObject(); params.put("file","D:\\01-模板合同文件\\test-templte.pdf"); params.put("sealId","0"); params.put("signPos","{" + "\"posX\":100," + "\"posY\":0," + "\"addSignTime\":\"true\"," + "\"posPage\":\"1-10\"," + "\"posType\":\"1\"," + "\"key\":\"关键字\"" + "}"); params.put("signType","Key"); InputStream inputStream = null; String result = ""; CloseableHttpClient httpClient = HttpClients.createDefault(); try { HttpPost httpPost = new HttpPost(url); RequestConfig defaultRequestConfig = RequestConfig.custom(). setConnectTimeout( 5000 ) .setConnectionRequestTimeout( 5000 ) .setSocketTimeout( 15000 ) .build(); httpPost.setConfig( defaultRequestConfig ); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.setMode( HttpMultipartMode.BROWSER_COMPATIBLE ); //字符编码 builder.setCharset(Charset.forName("UTF-8")); Iterator iter = params.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); if (entry.getKey().toString().equalsIgnoreCase("file") ) { String filePath = params.get("file").toString(); inputStream = new FileInputStream(filePath); String fileName = filePath.substring(filePath.lastIndexOf("/") + 1); builder.addBinaryBody("file", inputStream, ContentType.MULTIPART_FORM_DATA, fileName);// 设置流参数 inputStream = new FileInputStream(filePath); byte[] bytes = new byte[0]; try { bytes = read(inputStream); } catch (IOException e) { e.printStackTrace(); } String fileHash = new String(Base64.encodeBase64(bytes)); System.out.println("fileHash:"+fileHash); params.put("file",fileHash); } else { builder.addTextBody(entry.getKey().toString(), entry.getValue().toString(), ContentType.create("text/plain", Consts.UTF_8)); } } //转换入参信息,将map的入参转为url参数 String signParamsStr = parameterText(params,"&", "sign_type","sign", "key", "signature"); System.out.println("signParamsStr:"+signParamsStr); String sign = getSignature(signParamsStr, "项目id对应的密钥projectsecret","HmacSHA256","UTF-8"); httpPost.addHeader("X-timevale-signature", sign); System.out.println("params:"+ params); System.out.println("[url]:" + url); System.out.println("[request]:" + params); //HttpEntity HttpEntity entity = builder.build(); httpPost.setEntity(entity); CloseableHttpResponse resp = null; //返回结果 resp = httpClient.execute( httpPost ); StatusLine line = resp.getStatusLine(); // 结果 HttpEntity httpEntity = resp.getEntity(); if (httpEntity != null) { result = EntityUtils.toString( httpEntity, Charset.forName( "UTF-8" ) ); System.out.println( "返回响应码: " + line.getStatusCode()+" "+"返回结果: " + result ); } } catch (Exception e) { e.printStackTrace(); } finally { try { httpClient.close(); } catch (IOException e) { e.printStackTrace(); } } System.err.println("result" + result); } public static byte[] read(InputStream inStream) throws IOException, NoSuchAlgorithmException { byte[] md5Bytes = null; MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] buffer = new byte[1024]; int len = 0; while((len=inStream.read(buffer))!=-1){ md5.update(buffer, 0, len); } md5Bytes = md5.digest(); inStream.close(); return md5Bytes; } /*** * 获取请求签名值 * * @param data * 加密前数据 * @param key * 密钥 * @param algorithm * HmacMD5 HmacSHA1 HmacSHA256 HmacSHA384 HmacSHA512 * @param encoding * 编码格式 * @return HMAC加密后16进制字符串 * @throws Exception */ public static String getSignature(String data, String key, String algorithm, String encoding) { Mac mac = null; try { mac = Mac.getInstance(algorithm); SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(encoding), algorithm); mac.init(secretKey); mac.update(data.getBytes(encoding)); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); System.out.println("获取Signature签名信息异常:" + e.getMessage()); return null; } catch (UnsupportedEncodingException e) { e.printStackTrace(); System.out.println("获取Signature签名信息异常:" + e.getMessage()); return null; } catch (InvalidKeyException e) { e.printStackTrace(); System.out.println("获取Signature签名信息异常:" + e.getMessage()); return null; } return byte2hex(mac.doFinal()); } /*** * 将byte[]转成16进制字符串 * * @param data * * @return 16进制字符串 */ public static String byte2hex(byte[] data) { StringBuilder hash = new StringBuilder(); String stmp; for (int n = 0; data != null && n < data.length; n++) { stmp = Integer.toHexString(data[n] & 0XFF); if (stmp.length() == 1) hash.append('0'); hash.append(stmp); } return hash.toString(); } /** * 把数组所有元素排序,并按照“参数=参数值”的模式用“@param separator”字符拼接成字符串 * * @param parameters 参数 * @param separator 分隔符 * @param ignoreKey 需要忽略添加的key * @return 去掉空值与签名参数后的新签名,拼接后字符串 */ public static String parameterText(Map parameters, String separator, String... ignoreKey) { if (parameters == null) { return ""; } StringBuffer sb = new StringBuffer(); if (null != ignoreKey) { Arrays.sort(ignoreKey); } // TODO 2016/11/11 10:14 author: egan 已经排序好处理 for (Map.Entry<String, Object> entry : (Set<Map.Entry<String, Object>>) parameters.entrySet()) { Object v = entry.getValue(); if (null == v || "".equals(v.toString().trim()) || (null != ignoreKey && Arrays.binarySearch(ignoreKey, entry.getKey()) >= 0)) { continue; } sb.append(entry.getKey()).append("=").append(v.toString().trim()).append(separator); } if (sb.length() > 0 && !"".equals(separator)) { sb.deleteCharAt(sb.length() - 1); } System.out.println("排序前:"+ sb.toString()); return sb.toString(); }
4、PHP开发语言示例
//对于请求数据data 转为json字符串 $pa=json_encode($param); $signature = getSignature($pa,PROJECT_SECRET(密钥)); /计算请求签名值 function getSignature($message, $projectSecret) { $signature = hash_hmac('sha256', $message, $projectSecret, FALSE); return $signature; }
5、.NET获取示例
// 请求参数-JSON字符串 string data = jsonObject.ToString(); // 请求签名值 string signature =GetSignature(data, SceneConfig.PROJECTSECRET); 具体方法: /// <summary> /// HmacSHA256 加密 /// </summary> /// <param name="secret">projectSecret</param> /// <param name="data">请求的JSON参数</param> /// <returns></returns> public static string GetSignature(string data, string secret) { byte[] keyByte = Encoding.UTF8.GetBytes(secret); byte[] messageBytes = Encoding.UTF8.GetBytes(data); using (var hmacsha256 = new HMACSHA256(keyByte)) { byte[] hashmessage = hmacsha256.ComputeHash(messageBytes); StringBuilder sb = new StringBuilder(); foreach (byte test in hashmessage) { sb.Append(test.ToString("x2")); } return sb.ToString(); } } }