参数及说明:
processCode:表单列表里面的编码
接口调用:
注意:流程发起成功之后,需要存一下返回的流程实例id,这里的作用请结合《十二、通知外部系统流程状态变化》查阅
发起之后可以在流程里面看到:
发起流程如果提供给外部使用,特别是互联网接口,请使用接口签名;
方案:
请求头携带参数appId+timestamp+nonce+sign,只有1.拥有合法的身份appId和正确的签名sign 2.timestamp10分钟内、nonce未被使用才能放行。这样就解决了身份验证和参数篡改问题,即使请求参数被劫持,由于获取不到appSecret(仅作本地加密使用,不参与网络传输),无法伪造合法的请求。
一个测试用例:
/**
* 第三方接口签名方法测试
* 主要实现以下两条:
* 1.防止篡改
* 参数签名
* 按照请求header的字母升序排列非空请求参数(包含appId),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA
* 按照请求body的字母升序排列非空请求参数,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringB
* (这里将参数header和体分开,避免参数覆盖-header里面的参数名和头参数key相同)
* stringA+stringB+appSecret得到字符串stringSignTemp;
* 对stringSignTemp进行sha256签名运算,并将得到的字符串所有字符转换为大写,得到sign值。
* <p>
* 2.重放攻击
* timestamp+nonce方案
* nonce指唯一流水号,用来标识每个被签名的请求。通过为每个请求提供一个唯一的标识符,服务器能够防止请求被多次使用(记录所有用过的nonce以阻止它们被二次使用)。
* 然而,对服务器来说永久存储所有接收到的nonce的代价是非常大的。可以使用timestamp来优化nonce的存储。
* 假设允许客户端和服务端最多能存在10分钟的时间差,同时追踪记录在服务端的nonce集合。当有新的请求进入时,首先检查携带的timestamp是否在10分钟内,
* 如超出时间范围,则拒绝,然后查询携带的nonce,如存在已有集合,则拒绝。否则,记录该nonce,并删除集合内时间戳大于10分钟的nonce(可以使用redis的expire,新增nonce的同时设置它的超时失效时间为10分钟)。
* <p>
* <p>
* 请求头携带参数appId+timestamp+nonce+sign,只有1.拥有合法的身份appId和正确的签名sign 2.timestamp10分钟内、nonce未被使用才能放行。
* 这样就解决了身份验证和参数篡改问题,即使请求参数被劫持,由于获取不到appSecret(仅作本地加密使用,不参与网络传输),无法伪造合法的请求。
* <p>
* 还可以加上IP白名单校验
*/
@Test
public void createSign() {
//appId和加密密钥,由服务提供方给
String appId = "100001";
String appSecret = "80f740fec04a5e7daebd8dd02ca4f69c";
//参数-header
Map<String, Object> header = new HashMap<>();
//app标识-标识来自于哪个第三方应用
header.put("appId", appId);
//使用雪花算法生成流水id
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
header.put("nonce", snowflake.nextId());
//时间戳-接口有效期,比如此调用超过10分钟失效
header.put("timestamp", System.currentTimeMillis());
//根据参数排序后拼接为字符串,常用于签名
String stringA = MapUtil.sortJoin(header, "&", "=", true);
log.info("{}", stringA);
//参数-body
Map<String, Object> body = new HashMap<>();
body.put("name", "hello");
body.put("home", "world");
body.put("work", "java");
String stringB = MapUtil.sortJoin(body, "&", "=", true);
log.info("{}", stringB);
//生成签名--使用摘要算法
String stringSignTemp = stringA + stringB + appSecret;
//头数据字符串+请求参数字符串+key
String sign = SecureUtil.sha256(stringSignTemp);
log.info("{}", sign.toUpperCase());
}