为了防止您的接口被其他人调用造成数据异常的情况,小鱼易连提供api(websocket不支持)回调事件签名解决方案
签名由(企业回调秘钥+返回体) 通过国密SM3计算而得并附加到您注册的地址后面,key为sign,因此您注册时应避免sign字段作为参数
eg1:
您注册的地址为:www.xylink.com?x=1
小鱼易连调用时为:www.xylink.com?x=1&sign=xxx
eg2:
您注册的地址为:www.xylink.com
小鱼易连调用时为:www.xylink.com?sign=xxx
企业回调秘钥:获取方式请查看回调秘钥管理
返回体:为您提供的地址收到的请求
1、Windows平台下计算的签名可能和Linux平台下计算的签名不一致,因此测试时如果遇到Windows平台下计算出来的签名不正确的情况下请在Linux平台进行计算。
2、此逻辑在5.2最新版及以上版本支持
//获取参数中的签名
String sign = request.getParam("sign")
//您计算出来的签名
String signCalc = SM3Digest(您收到的返回体的json字符串 + 回调秘钥)
if(signCalc.equals(sign)){
//do some business
}else{
logger.warn("illegal sign")
break;
}
1、引用jar包到项目中
下载最新的jar包,使用包中的方法进行计算
方法String com.xylink.util.EncryptUtil.calcSign(String token, String content) throws JsonProcessingException
类目 | 分类 | 描述 |
token | 参数 | 回调签名的token |
content | 参数 | 需要计算签名的body内容 |
返回 | 返回为String | |
异常 | JSON解析异常 |
2、拷贝代码到项目中
可以直接拷贝到自己的项目中
import cn.hutool.core.util.HexUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.Security;
/**
*@Classname SM4Util
*@Date 2021/9/18 5:47 下午
*@Created by anan
*@Description description here
*需要引入包:
*implementation 'org.bouncycastle:bcprov-jdk15on:1.57'
*/
public class EncryptUtil {
private static final Logger logger = LoggerFactory.getLogger(EncryptUtil.class);
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
*国密SM3加盐hash
*@param key
*@param value
*@return
*/
public static String sm3SaltedHashValue(String key, String value) {
logger.info("key: {}, value:{}", key, value);
//加盐
byte[] passwdbyte = arraycat(key.getBytes(),value.getBytes());
//SM3计算
SM3Digest mdDigest=new SM3Digest();
mdDigest.update(passwdbyte,0,passwdbyte.length);
byte[] result=new byte[mdDigest.getDigestSize()];
mdDigest.doFinal(result, 0);
return HexUtil.encodeHexStr(result);
}
/**
* 拼接buf1和buf2数组
*/
public static byte[] arraycat(byte[] buf1,byte[] buf2) {
byte[] bufret=null;
int len1=0;
int len2=0;
if(buf1!=null) {
len1=buf1.length;
}
if(buf2!=null) {
len2=buf2.length;
}
if(len1+len2>0) {
bufret=new byte[len1+len2];
}
if(len1>0) {
System.arraycopy(buf1,0,bufret,0,len1);
}
if(len2>0) {
System.arraycopy(buf2,0,bufret,len1,len2);
}
return bufret;
}
/**
* 计算签名
*
* @param token
* @param content 内容
* @return
*/
public static String calcSign(String token, String content) {
//签名计算
try {
content = content.length() > 100 ? content.substring(0, 100) : content;
String encrypt = EncryptUtil.sm3SaltedHashValue(token, content);
return encrypt.substring(0, 30);
} catch (Exception e) {
logger.error("failed to calcSign, canbe igonre", e);
}
return "";
}
public static void main(String[] args) throws JsonProcessingException {
String token = "1c104121ff95b265e26f3f64a36330d8a5214c96a75a448ed0da1ab4b0fd4354";
String content = "{\"eventType\":\"NewUserCall\",\"data\":{\"callerNumber\":\"+86-19800000235\",\"calleeNumber\":\"9090317356\",\"callerType\":\"user\",\"calleeType\":\"meetingRoom\",\"callerId\":\"26471418\",\"calleeId\":\"9090317356\",\"callerName\":\"TestXiAn00235\",\"calleeName\":\"hongyue 共享会议室\",\"callerExternalUserId\":null,\"calleeExternalUserId\":null,\"callerDeviceType\":5,\"calleeDeviceType\":0,\"callStatus\":\"end\",\"time\":1639382663053,\"userCountOfSameConf\":46,\"meetingId\":\"103-bj1-testqaSig1ms-1161979400250\",\"caller\":true},\"code\":200,\"msgId\":\"9eff78e8-1d6b-4390-935c-34f98fc95cc8\",\"timestamp\":1639382663119}";
ObjectMapper mapper = new ObjectMapper();
Object socketMessage = mapper.readValue(content, Object.class);
System.out.println(socketMessage.toString());
String content1 = mapper.writeValueAsString(socketMessage);
System.out.println(content1);
String sign = calcSign(token, content1);
System.out.println(sign);
}
}
REST URL
POST https://sdk.xylink.com/api/rest/external/{version}/enterprise/config?enterpriseId=XXX&signature=XXX
请求参数说明
业务参数 | 位置 | 说明 | 是否必须 | 初始平台 |
enterpriseId | Query | 企业id | 必传 | 5.2最新版 |
signature | Query | API签名,参见签名规则 | 签名鉴权1.0(旧):是 签名鉴权2.0:否 | 5.2最新版 |
version | Path | 接口版本 | v1版本为历史版本,接口请求内容及返回结构会永久保留 v2及以后版本为活跃版本,请求参数及返回参数会随着业务的需要而扩充,但是字段不会减少,结构也不会变化,因此需要用户兼容字段增加的情形 | 5.2最新版 |
configName | body | 配置名称,callbackSignToken | 不可变 | 5.2最新版 |
configValue | body | 秘钥值 | 生成uuid,0~32位数字、字母或者-_ | 5.2最新版 |
comment | body | 纪要 | 配置值说明 | 5.2最新版 |
请求消息体示例(Json)
{
"configName": "callbackSignToken",
"configValue": "{替换为自己生成的配置值}",
"comment": "初始化 "
}
成功时返回httpcode 200,失败时返回小鱼RESTAPI错误码
注:此接口会在无初始值的情况下初始化值并返回(如果之前使用的token来自企业管理平台,请谨慎操作,调用接口后企业管理平台的token对于回调签名不再生效)
REST URL
GET https://sdk.xylink.com/api/rest/external/{version}/enterprise/config?enterpriseId=XXX&signature=XXX&configName=callbackSignToken
请求参数说明
业务参数 | 位置 | 说明 | 是否必须 | 初始平台 |
enterpriseId | Query | 企业id | 必传 | 5.2最新版 |
signature | Query | API签名,参见签名规则 | 签名鉴权1.0(旧):是 签名鉴权2.0:否 | 5.2最新版 |
version | Path | 接口版本 | v1版本为历史版本,接口请求内容及返回结构会永久保留 v2及以后版本为活跃版本,请求参数及返回参数会随着业务的需要而扩充,但是字段不会减少,结构也不会变化,因此需要用户兼容字段增加的情形 | 5.2最新版 |
成功时返回结果数据,失败时返回小鱼RESTAPI错误码
[
{
"configName": "callbackSignToken",
"configValue": "xylink_callback_token",
"comment": "初始化token "
}
]
返回值说明
业务参数 | 说明 |
configName | 配置名称 |
configValue | 秘钥值 |
comment | 备注 |