帮助中心
所有文章
使用指南
事件订阅设置
最后更新于 2023/04/26   阅读数 337

概述

只需要告诉订阅蜂该向哪里(URL)发送事件以及事件类型,当事件发生时,订阅蜂便会以 HTTP POST 请求的方式将事件内容以 json 格式进行推送。​

事件订阅可以让应用及时响应里的动作,从而进行高度整合。

事件订阅流程​

事件订阅包含两部分:配置请求网址及事件订阅接受并响应事件。​

  • 事件订阅指开发者订阅所需的事件,只有订阅后才能收到触发的事件的回执 ​

  • 请求网址用于接收订阅的事件回执 ​

流程详解如下所示:​

  1. 设置事件回调的请求网址。 ​

  1. 保存网址时,订阅蜂服务器会向应用服务器发送请求,验证网址的所有权。 ​

  1. 应用服务器收到请求,解析出请求中的 subscribe,在规定时间内响应给订阅蜂服务器。 ​

  1. 订阅蜂服务器响应给开发者请求网址配置成功。 ​

  1. 在事件订阅列表中,订阅感兴趣的事件。 ​

  1. 事件触发后,订阅蜂服务器感知到事件,发送事件内容给应用服务器。 ​

  1. 应用服务器收到事件内容后,在规定的时间内作出响应,返回给订阅蜂服务器。 ​

配置请求网址及事件订阅 

【配置--> 基础设置--> 事件订阅】

  1. 配置请求网址​

 

  输入请求地址后,点击 保存

 配置请求地址后,订阅蜂会向请求地址推送一个 application/json 格式的 POST 请求,该 POST 请求用于验证所配置的请求地址的合法性。该 POST 请求中会携带一个subscribe字段,应用需要在 1 秒内,将接收到的 subscribe 值原样返回给订阅蜂。

订阅蜂会推送加密后的 POST 请求(请求头: X-Bee-Signature、X-Bee-Request-Timestamp、X-Bee-Request-Nonce):

{    "encryptedEvent": "ds3da3sj32421lkkld4s5ao" // 加密字符串}
1
plaintext

应用需要先解密,然后解密后并在 1 秒内原样返回subscribe值作为响应。有关解密方法的详细介绍,请参考下面的接受并响应事件​

解密后的 encryptedEvent 值:

 "subscribe"
1
plaintext

响应示例(同样的方式加密返回):

{
"X-Bee-Signature":"生成的数字签名",
"X-Bee-Request-Nonce":"随机数变量",
"X-Bee-Request-Timestamp":"时间戳",
"encrypedEvent":"subscribe加密后的字符串"
}
1
2
3
4
5
6
plaintext

2. 订阅事件

订阅“审批” 和 “客户反馈”事件,只有订阅了才会触发事件回调。

接受并响应事件​

订阅的事件发生时,订阅蜂将会通过 HTTP POST 请求发送 json 格式的事件数据到预先配置的请求网址URL。​

​💡 收到此请求后,需要在1秒内以响应该请求并以加密的形式返回固定字符串subscribe值,否则订阅蜂会视此次推送失败并以 5s、5m、1h、6h 的间隔重新推送事件,最多重试 4 次。​

  • 为了避免同一个事件被处理多次,需要使用 event_id对事件的唯一性进行检查。 ​

  • 在进行业务逻辑处理前请先进行解密(参考后面的加解密方法)。 ​ ​

响应事件的请求示例(请求头: X-Bee-Signature、X-Bee-Request-Timestamp、X-Bee-Request-Nonce)

(加密)

{
"encrypedEvent":"FIAfJPGRmFZWkaxPQ1XrJZVbv2JwdjfLk4jx0k/U1deAqYK3AXOZ5zcHt/cC4ZNTqYwWUW/EoL+b2hW/C4zoAQQ5CeMtbxX2zHjm+E4nX/Aww+FHUL6iuIMaeL2KLxqdtbHRC50vgC2YI7xohnb3KuCNBMUzLiPeNIpVdnYaeteCmSaESb+AZpJB9PExzTpRDzCRv+T6o5vlzaE8UgIneC1sYu85BnPBEMTSuj1ZZzfdQi7ZW992Z4dmJxn9e8FL2VArNm99f5Io3c2O4AcNsQENNKtfAAxVjCqc3mg5jF0nKabA+u/5vrUD76flX1UOF5fzJ0sApG2OEn9wfyPDRBsApn9o+fceF9hNrYBGsdtZrZYyGG387CGOtKsuj8e2E8SNp+Pn4E9oYejOTR+ZNLNi+twxaXVlJhr6l+RXYwEiMGQE9zGFBD6h2dOhKh3W84p1GEYnSRIz1+9/Hp66arjC7RCrhuW5OjCj4QFEQJiwgL45XryxHtiZ7JdAlPmjVsL03CxxFZarzxzffryrWUG3VkRdHRHbTsC34+ScoL5MTDU1QAWdqUC1T7xT0lCvQELaIhBTXAYrznJl6PlA83oqlMxpHh0gZBB1jFbfoUr7OQbBs1xqzpYK6Yjux6diwpQB1zlZErYJUfCqK7G/zI9yK/60b4HW0k3M+AvzMcw="
}
1
2
3
plaintext

事件安全校验​

业务方收到订阅蜂推送的事件时,如果需要确保这个请求的来源是订阅蜂而非伪造,可以按照如下方式校验。​

  1. 获取 encrypt_key: ​

  • 访问配置 > 基础设置 > 事件订阅, 在事件订阅页面中查看 encrypt_key。 ​

  1. 校验请求来源: ​

  • 将请求头 X-Bee-Request-Timestamp、X-Bee-Request-Nonce 与encrypt_key拼接后 按照 encode('utf-8')编码得到 byte[] b1,再拼接上 body, 得到一个 byte[] b。 ​

  • 将 b 用 sha256 加密,得到字符串 s, 业务方校验 s 是否和请求头 X-Bee-Signature 一致。 ​

  • Java 代码示例: ​

/**
     * 数字签名
     *
     * @param token     token
     * @param timestamp 时间戳
     * @param nonce     随机串
     * @param encrypt   加密文本
     * @return
     * @throws DingCallbackCrypto.DingTalkEncryptException
     */
    public String getSignature(String token, String timestamp, String nonce, String encrypt){
            String[] array = new String[]{token, timestamp, nonce, encrypt};
            Arrays.sort(array);
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < 4; i++) {
                sb.append(array[i]);
            }
            String str = sb.toString();
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();

            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] &amp; 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
    }​
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
plaintext

事件列表与事件的详细结构

事件分类

事件名称

触发条件

报价单

审批

eventType: quote_approval

报价审批发生时

(提交,撤回,通过,驳回)

客户反馈

eventType: quote_customer_feedback

客户反馈报价单时

(同意,拒绝)

创建

eventType: quote_create

报价创建时

客户

创建

eventType: customer_create

客户创建时

商机

创建

eventType: opp_create

商机创建时

审批

{

  • "context": {

  •    "eventId": "f7984f25108f8137722bb63cee927e66", // 事件的唯一标识

"token": "066zT6pS4QCbgj5Do145GfDbbagCHGgF", // 即 签名 Token

"createTime": "1603977298000000", // 事件发送的时间

"eventType": "quote_approval", // 事件类型

"tenantId": "xxxxxxx", // 租户

},

"event":{

"quoteId": "x5984f25108f8137722bb63cee927e66", // 报价单ID

"quoteName": "订阅蜂报价单", // 报价单名称

"processId": "x5984f25108f8137722bb63cee927e66", // 审批流程定义ID

"instanceId": "x5984f25108f8137722bb63cee927e66", // 审批实例ID

"status": "Pending", //审批状态 (Pending审批中, Approved审批通过, Refused审批驳回,Reverted审批撤回)

  • "nodes": [{// 审批任务节点信息

  •   "id": "x5984f25108f8137722bb63cee927e66", // 审批任务节点ID

  •   "name": "财务审批", // 审批任务节点名称 (自定义的,如:产品审批,财务审批等)

  •    "result": "refused", // 审批任务节点意见(approved同意,驳回refused)

  •    "comments": "ARR折扣高于5%不合理", // 审批任务节点评论

  •   "staffId": "x5984f25108f8137722bb63cee927e66", //审批人ID     "staffName": "Ella", //审批人名字      "startTime": "1603977298000000", //审批任务开始事件      "endTime": "1603977298000000", //审批任务结束时间     }

]

"quoteDetailRequest": "https://www.dingyuefeng.cn/api/v1/quotes/5984f25108f8137722bb63cee927e66"

}

}

客户反馈

{

"context": {

"eventId": "f7984f25108f8137722bb63cee927e66", // 事件的唯一标识

"token": "066zT6pS4QCbgj5Do145GfDbbagCHGgF", // 即 签名 Token

"createTime": "1603977298000000", // 事件发送的时间

"eventType": "quote_customer_feedback", // 事件类型

"tenantId": "xxxxxxx", // 租户

},

"event":{

"quoteId": "x5984f25108f8137722bb63cee927e66", // 报价单ID

"result": "refused", // 客户意见 (accepted 接受报价 | refused 驳回需要再沟通)

"comments": "折扣需要调整1个百分点", // 客户反馈的内容

"createTime": "1603977298000000", // 客户反馈的时间

"quoteDetailRequest": "https://www.dingyuefeng.cn/api/v1/quotes/5984f25108f8137722bb63cee927e66"

}

}​

报价创建

{

"context": {

"eventId": "f7984f25108f8137722bb63cee927e66", // 事件的唯一标识

"token": "066zT6pS4QCbgj5Do145GfDbbagCHGgF", // 即 签名 Token

"createTime": "1603977298000000", // 事件发送的时间

"eventType": "quote_create", // 事件类型

"tenantId": "xxxxxxx", // 租户

},

"event":{

"quoteId": "x5984f25108f8137722bb63cee927e66", // 报价单ID

"createTime": "1603977298000000", // 报价创建的时间

"quoteDetailRequest": "https://www.dingyuefeng.cn/api/v1/quotes/5984f25108f8137722bb63cee927e66"

}

}​

客户创建

{

"context": {

"eventId": "f7984f25108f8137722bb63cee927e66", // 事件的唯一标识

"token": "066zT6pS4QCbgj5Do145GfDbbagCHGgF", // 即 签名 Token

"createTime": "1603977298000000", // 事件发送的时间

"eventType": "customer_create", // 事件类型

"tenantId": "xxxxxxx", // 租户

},

"event":{

"customerId": "x5984f25108f8137722bb63cee927e66", // 客户ID

"createTime": "1603977298000000", // 客户创建的时间

"customerDetailRequest": "https://www.dingyuefeng.cn/api/v1/customers/5984f25108f8137722bb63cee927e66"

}

}​

商机创建

{

"context": {

"eventId": "f7984f25108f8137722bb63cee927e66", // 事件的唯一标识

"token": "066zT6pS4QCbgj5Do145GfDbbagCHGgF", // 即 签名 Token

"createTime": "1603977298000000", // 事件发送的时间

"eventType": "opp_create", // 事件类型

"tenantId": "xxxxxxx", // 租户

},

"event":{

"oppId": "x5984f25108f8137722bb63cee927e66", // 商机ID

"createTime": "1603977298000000", // 商机创建的时间

"oppDetailRequest": "https://www.dingyuefeng.cn/api/v1/opportunities/5984f25108f8137722bb63cee927e66"

}

}​

解密事件回调

当订阅的消息事件发生时,就会触发回调,应用将收到加密的事件内容,示例如下

​JSON{

"encryptedEvent":"FIAfJPGRmFZWkaxPQ1XrJZVbv2JwdjfLk4jx0k/U1deAqYK3AXOZ5zcHt/cC4ZNTqYwWUW/EoL+b2hW/C4zoAQQ5CeMtbxX2zHjm+E4nX/Aww+FHUL6iuIMaeL2KLxqdtbHRC50vgC2YI7xohnb3KuCNBMUzLiPeNIpVdnYaeteCmSaESb+AZpJB9PExzTpRDzCRv+T6o5vlzaE8UgIneC1sYu85BnPBEMTSuj1ZZzfdQi7ZW992Z4dmJxn9e8FL2VArNm99f5Io3c2O4AcNsQENNKtfAAxVjCqc3mg5jF0nKabA+u/5vrUD76flX1UOF5fzJ0sApG2OEn9wfyPDRBsApn9o+fceF9hNrYBGsdtZrZYyGG387CGOtKsuj8e2E8SNp+Pn4E9oYejOTR+ZNLNi+twxaXVlJhr6l+RXYwEiMGQE9zGFBD6h2dOhKh3W84p1GEYnSRIz1+9/Hp66arjC7RCrhuW5OjCj4QFEQJiwgL45XryxHtiZ7JdAlPmjVsL03CxxFZarzxzffryrWUG3VkRdHRHbTsC34+ScoL5MTDU1QAWdqUC1T7xT0lCvQELaIhBTXAYrznJl6PlA83oqlMxpHh0gZBB1jFbfoUr7OQbBs1xqzpYK6Yjux6diwpQB1zlZErYJUfCqK7G/zI9yK/60b4HW0k3M+AvzMcw="

} ​

解密后就会得到:

​{

"context": {

       "eventId": "f7984f25108f8137722bb63cee927e66", // 事件的唯一标识

       "token": "066zT6pS4QCbgj5Do145GfDbbagCHGgF", // 即 签名 Token

        "createTime": "1603977298000000", // 事件发送的时间

        "eventType": "quote_approval", // 事件类型

        "tenantId": "xxxxxxx", // 租户

},

"event":{

      // 事件的详细信息,不同事件此处数据不同

      }

}​

加密/解密处理方式

AES加解密,加密方式为“AES/CBC/PKCS5Padding”

Java加解密示例代码工具类​

      
package cn.bee.modules.beesys.util;

import cn.bee.modules.approval.utils.DingCallbackCrypto;
import cn.bee.modules.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.spec.IvParameterSpec;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

//订阅蜂订阅通知加密解密方法
public class DingYueFengCallBackCrypto {
    private static final Base64 base64 = new Base64();

    private static final String ALGORITHM = "AES";
    private static final String AES_CBC_PADDING = "AES/CBC/PKCS5Padding";

    private byte[] aesKey;

    private String token;
    /**
     * 密钥长度
     */
    private static final Integer encryption_key_Length = 64;
    //token长度
    private static final Integer token_Length = 32;


    /**
     * 构造函数
     *
     * @param token         订阅蜂 事件订阅配置 签名token
     * @param encryptionKey 订阅蜂 事件订阅配置 加密密钥
     * @throws DingYueFengEncryptException
     */
    public DingYueFengCallBackCrypto(String token, String encryptionKey) throws DingYueFengEncryptException {
        if (null == encryptionKey || encryptionKey.length() != encryption_key_Length) {
            throw new DingYueFengEncryptException(DingYueFengEncryptException.AES_KEY_ILLEGAL);
        }
        if (token == null || token.length() != token_Length) {
            throw new DingYueFengEncryptException(DingYueFengEncryptException.TOKEN_ILLEGAL);
        }
        this.token = token;
        String encryptionKeyString = encryptionKey.substring(0, 43);
        aesKey = Base64.decodeBase64(encryptionKeyString + "=");
    }

    /**
     * 订阅蜂平台加密,返回map
     * @param text
     * @return
     * @throws DingYueFengEncryptException
     */
    public Map<String, String> setEncryptedMap(String text) throws DingYueFengEncryptException {
        return setEncryptedMap(text, System.currentTimeMillis(),randomData(16,true));
    }

    /**
     * 和订阅蜂平台加密,返回map
     *
     * @param text      传递的消息明文
     * @param timeStamp 时间戳
     * @param nonce     随机字符串
     * @return
     * @throws DingYueFengEncryptException
     */
    public Map<String, String> setEncryptedMap(String text, Long timeStamp, String nonce) throws DingYueFengEncryptException {
        if (null == text) {
            throw new DingYueFengEncryptException(DingYueFengEncryptException.ENCRYPTION_PLAINTEXT_ILLEGAL);
        }
        if (null == timeStamp) {
            throw new DingYueFengEncryptException(DingYueFengEncryptException.ENCRYPTION_TIMESTAMP_ILLEGAL);
        }
        if (null == nonce) {
            throw new DingYueFengEncryptException(DingYueFengEncryptException.ENCRYPTION_NONCE_ILLEGAL);
        }
        //进行加密
        // 加密
        String encrypt = setEncrypt(text);
        String signature = getSignature(token, String.valueOf(timeStamp), nonce, encrypt);
        Map<String, String> resultMap = new HashMap<String, String>();
        resultMap.put(EventSubscription.signature, signature);
        resultMap.put(EventSubscription.reqParam, encrypt);
        resultMap.put(EventSubscription.timeStamp, String.valueOf(timeStamp));
        resultMap.put(EventSubscription.nonce, nonce);
        return resultMap;
    }

    /**
     * 对明文进行加密
     *
     * @param text 加密的名文
     * @return
     * @throws DingYueFengEncryptException
     */
    private String setEncrypt(String text) throws DingYueFengEncryptException {
        try {
            javax.crypto.SecretKey secretKey = new javax.crypto.spec.SecretKeySpec(aesKey, ALGORITHM);
            javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(AES_CBC_PADDING);
            IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
            cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, secretKey, iv);
            // 获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
            byte[] byteEncode = text.getBytes(java.nio.charset.StandardCharsets.UTF_8);
            // 根据密码器的初始化方式加密
            byte[] byteAES = cipher.doFinal(byteEncode);
            // 将加密后的数据转换为字符串
            return base64.encodeToString(byteAES);
        } catch (Exception e) {
            throw new DingYueFengEncryptException(DingYueFengEncryptException.COMPUTE_ENCRYPT_TEXT_ERROR);
        }
    }

    /**
     * 对密文进行解密
     *
     * @param msgSignature 签名串
     * @param timeStamp    时间戳
     * @param nonce        随机串
     * @param encryptMsg   密文
     * @return
     */
    public String getDecryptMessage(String msgSignature, String timeStamp, String nonce, String encryptMsg) throws DingYueFengEncryptException {
        //校验签名
        String sinature = getSignature(token, timeStamp, nonce, encryptMsg);
        if (!sinature.equals(msgSignature)) {
            throw new DingYueFengEncryptException(DingYueFengEncryptException.COMPUTE_SIGNATURE_ERROR);
        }
        //解密数据
        String result = getDecrypt(encryptMsg);
        return result;
    }

    /**
     * 对密文进行解密
     *
     * @param encryptMsg 需要解密的密文
     * @return
     * @throws DingYueFengEncryptException
     */
    private String getDecrypt(String encryptMsg) throws DingYueFengEncryptException {
        try {
            javax.crypto.SecretKey secretKey = new javax.crypto.spec.SecretKeySpec(aesKey, ALGORITHM);
            javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(AES_CBC_PADDING);
            IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
            cipher.init(javax.crypto.Cipher.DECRYPT_MODE, secretKey, iv);

            // 将加密并编码后的内容解码成字节数组
            byte[] byteContent = base64.decodeBase64(encryptMsg);
            // 解密
            byte[] byteDecode = cipher.doFinal(byteContent);
            return new String(byteDecode, java.nio.charset.StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new DingYueFengEncryptException(DingYueFengEncryptException.COMPUTE_DECRYPT_TEXT_ERROR);
        }

    }

    /**
     * 数字签名
     *
     * @param token     token
     * @param timestamp 时间戳
     * @param nonce     随机串
     * @param encrypt   加密文本
     * @return
     * @throws DingCallbackCrypto.DingTalkEncryptException
     */
    public String getSignature(String token, String timestamp, String nonce, String encrypt)
            throws DingYueFengEncryptException {
        try {
            String[] array = new String[]{token, timestamp, nonce, encrypt};
            Arrays.sort(array);
            System.out.println(JSON.toJSONString(array));
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < 4; i++) {
                sb.append(array[i]);
            }
            String str = sb.toString();
            System.out.println(str);
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();

            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] &amp; 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
        } catch (Exception e) {
            throw new DingYueFengEncryptException(DingYueFengEncryptException.COMPUTE_SIGNATURE_ERROR);
        }
    }


    public static class DingYueFengEncryptException extends Exception {
        public static final int SUCCESS = 0;
        public static final int ENCRYPTION_PLAINTEXT_ILLEGAL = 100001;
        public static final int ENCRYPTION_TIMESTAMP_ILLEGAL = 100002;
        public static final int ENCRYPTION_NONCE_ILLEGAL = 100003;
        public static final int AES_KEY_ILLEGAL = 100004;
        public static final int SIGNATURE_NOT_MATCH = 100005;
        public static final int COMPUTE_SIGNATURE_ERROR = 100006;
        public static final int COMPUTE_ENCRYPT_TEXT_ERROR = 100007;
        public static final int COMPUTE_DECRYPT_TEXT_ERROR = 100008;
        public static final int TOKEN_ILLEGAL = 100009;

        private static Map<Integer, String> msgMap = new HashMap();
        private Integer code;

        static {
            msgMap.put(0, "成功");
            msgMap.put(100001, "加密明文文本非法");
            msgMap.put(100002, "加密时间戳参数非法");
            msgMap.put(100003, "加密随机字符串参数非法");
            msgMap.put(100004, "不合法的encryption key");
            msgMap.put(100005, "签名不匹配");
            msgMap.put(100006, "签名计算失败");
            msgMap.put(100007, "计算加密文字错误");
            msgMap.put(100008, "计算解密文字错误");
            msgMap.put(100009, "不合法的token");
        }

        public Integer getCode() {
            return this.code;
        }

        public DingYueFengEncryptException(Integer exceptionCode) {
            super((String) msgMap.get(exceptionCode));
            this.code = exceptionCode;
        }
    }

    public static String randomData(int length, boolean... ma) {
        Random random = new Random();
        //定义一个字符串(A-Z,a-z,0-9)即62位;
        String str = "zxcvbnmlkjhgfdsaqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
        //由Random生成随机数
        StringBuffer sb = new StringBuffer();
        //长度为几就循环几次
        for (int i = 0; i < length; ++i) {
            //产生0-61的数字
            int number = random.nextInt(62);
            //将产生的数字通过length次承载到sb中
            sb.append(str.charAt(number));
        }
        //将承载的字符转换成字符串
        return ma.length != 0 ? ma[0] ? sb.toString().toUpperCase() : sb.toString().toLowerCase() : sb.toString();
    }

    //加密测试算法
    public static void main(String[] args) {
        try {
            String message = "abcedewwsewwe";
            DingYueFengCallBackCrypto crypto = new DingYueFengCallBackCrypto(RandomUtil.randomData(32, true), RandomUtil.randomData(encryption_key_Length, true));
            Map<String, String> stringStringMap = crypto.setEncryptedMap(message, System.currentTimeMillis(), RandomUtil.randomData(16, true));
            String msg_signature = stringStringMap.get(EventSubscription.signature);
            String timeStamp = stringStringMap.get(EventSubscription.timeStamp);
            String encrypt = stringStringMap.get(EventSubscription.reqParam);
            String nonce = stringStringMap.get(EventSubscription.nonce);
            String decryptMessage = crypto.getDecryptMessage(msg_signature, timeStamp, nonce, encrypt);
            System.out.println(decryptMessage);
        } catch (DingYueFengEncryptException e) {
            e.printStackTrace();
        }
    }
}

    


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
plaintext

Java 加解密测试类

public class EventSubscription {

    /**
     * 时间戳变量
     */
    public static final String timeStamp = "X-Bee-Request-Timestamp";
    /**
     * 随机数变量
     */
    public static final String nonce = "X-Bee-Request-Nonce";
    /**
     * 签名
     */
    public static final String signature = "X-Bee-Signature";
    /**
     * 发送加密后的参数名称
     */
    public static final String reqParam="encrypedEvent";
    /**
     * 发送返回结果值
     */
    public static final String result="subscribe";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
plaintext
@PostMapping("test")
@ApiOperation("测试")
public Map<String, String> test(HttpServletRequest httpServletRequest, @RequestParam(value = "encrypedEvent") String encrypedEvent) {
    String signature = httpServletRequest.getHeader(EventSubscription.signature);
    String noce = httpServletRequest.getHeader(EventSubscription.nonce);
    String timeStamp = httpServletRequest.getHeader(EventSubscription.timeStamp);
    System.out.println("signature:" + signature);
    System.out.println("encry:" + encrypedEvent);
    try {
        //解密数据
        DingYueFengCallBackCrypto crypto = new DingYueFengCallBackCrypto("RVIKZSTDW9MODM6UPEWQAHQIDUMXXJYB","FW3ENVZAOXUEEYJVASNMCT27TO3IGJBK2MNG8KQMN7JSTKL2H8HB0QQNEAXX2M99");
        String decryptMessage = crypto.getDecryptMessage(signature, timeStamp, noce, encrypedEvent);
        System.out.println("解密后结果:"+decryptMessage);
        //todo 处理业务数据

        //结果回密返回
        return crypto.setEncryptedMap("subscribe");
    } catch (DingYueFengCallBackCrypto.DingYueFengEncryptException e) {
        e.printStackTrace();
    }
    return null;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
plaintext

响应事件回调后返回示例(返回subscribe)

响应示例(同样的方式加密返回):

{
"X-Bee-Signature":"生成的数字签名",
"X-Bee-Request-Nonce":"随机数变量",
"X-Bee-Request-Timestamp":"时间戳",
"encrypedEvent":"加密后的subscribe"
}
1
2
3
4
5
6
plaintext

encrypedEvent解密得到:

“subscribe"
1
plaintext
本篇目录

概述

配置请求网址及事件订阅 

事件列表与事件的详细结构

解密事件回调​