Welink接口回调及事件列表

举报
云会议运营喵大人 发表于 2020/02/13 18:09:21 2020/02/13
【摘要】 接口回调1、填写回调地址填写回调地址,例如:https://example.com/callback。 为了安全,强烈推荐使用https。2、验证回调参数以及解密数据WeLink每一次访问回调URL的时候将发出如下请求(下面链接中的值只是示例,并不代表真实值):POST https://您服务端部署的IP:您的端口/callback# 包含的json数据如下,其中encrypt是经过加密的消...

接口回调


1、填写回调地址

填写回调地址,例如:https://example.com/callback。 为了安全,强烈推荐使用https。

1581588503692238.png

2、验证回调参数以及解密数据

WeLink每一次访问回调URL的时候将发出如下请求(下面链接中的值只是示例,并不代表真实值):

POST https://您服务端部署的IP:您的端口/callback
# 包含的json数据如下,其中encrypt是经过加密的消息体(采用base64编码), 其中包含事件类型以及相应的一些数据
{"encrypt": "MDgwNTY0NDIxODUyMjA3OA==RTE3Q0E5MEI5RDIyRjlFRkJGNDg3NTYyQTA3ODI0NEE2OTU4NjE0NDJBRDg0OURDQTVDNEFBNTlEODM0NkQ1MjJFMkM1ODVENzlDMENGQTFDOThBRkRGNzc2QjU1MjYwNUMyNDk1NkE5Mjc0RjUxOTdFMDM5MkQzRDIzRTg4M0M1NUY5ODQxNTZFOTIwNEVBQ0EyMkJEMzkxNUExQkM2Q0NCN0EyODdE"}

encrypt内的数据为加密后的消息体,采用AES GCM算法,采用16字节的二进制偏移量(base64编码后就是24字节), 解密后的数据结构如下:

{
  "eventType": "xxx", 
  "timestamp": "1565167553",
  "xxx": "xxx"
}

为防止重放攻击,请务必判断timestamp(Unix的秒级时间戳)是否与您服务器的时间偏差在允许的时间范围内, 这里推荐30min内的时间是合理的。具体的解密见5代码参考。

3、返回结果

返回结果也需要进行带上timestamp加密,防止重放攻击以及验证回调是否被正确推送到应用。
应用返回原始数据结构应如下:

{
    "msg": "success",
    "timestamp": "1565167553"
}

经过AES GCM算法加密后(前16字节为偏移量),先进行base64编码(24字节)再和base64编码的密文拼接,拼成json格式。以上数据经过加密后,返回的结果应该为:

{"encrypt": "NjA0NTQ4MzM0MTExMjQ3NQ==MzhEMTY5RDI2Qjg4RjRDRTEwNUZBRTMyNjcxNTlCNDcyODUyNzEzQkUzOEU1Qzc3ODc2MjlFRkUzMzlGM0JCMTQ5QURBM0VCODA1QjExRTQ5NkI5Mjc0MzRCMTI3OTExNEI3RjU1RDRDNDNGNEE2MA=="}

需要注意的是,WeLink设置timestamp偏差超过30min时会认为返回值非法,推荐开发者直接以WeLink请求参数中timestamp返回即可。

4、回调类型

事件类型eventType请求示例原文
测试test{"encrypt": "xxx"}{"eventType": "test","timestamp": "1562752619"}
企业信息更改corpEditTenant{"encrypt": "xxx"}{"eventType": "corpEditTenant","timestamp": "1562752619", "tenantId": "xxx"}
人员信息更改corpEditUser{"encrypt": "xxx"}{"eventType": "corpEditUser","timestamp": "1562752619", "data":[{"userId": "id1", "tenantId": "xxx"},{"userId": "id2", "tenantId":"xxx"}]}
人员信息删除corpDelUser{"encrypt": "xxx"}{"eventType": "corpDelUser","timestamp": "1562752619", "data":[{"userId": "id1", "tenantId": "xxx"},{"userId": "id2", "tenantId":"xxx"}]}
部门信息更改corpEditDept{"encrypt": "xxx"}{"eventType": "corpEditDept","timestamp": "1562752619", "data":[{"deptCode": "id1", "tenantId": "xxx"},{"deptCode": "id2", "tenantId":"xxx"}]}
部门信息删除corpDelDept{"encrypt": "xxx"}{"eventType": "corpDelDept","timestamp": "1562752619", "data":[{"deptCode": "id1", "tenantId": "xxx"},{"deptCode": "id2", "tenantId":"xxx"}]}

人员信息变更包括人员新增和人员修改, 部门信息更改包括部门新增和部门修改
WeLink仅会通知发生内容变化的用户id或者部门id等,应用需要自行调用相关的查询接口,将接口返回的信息覆盖原来的信息。

5、代码参考

import java.security.Key;import java.security.NoSuchAlgorithmException;import java.security.SecureRandom;import java.util.Base64;import javax.crypto.Cipher;import javax.crypto.KeyGenerator;import javax.crypto.SecretKey;import javax.crypto.spec.GCMParameterSpec;import javax.crypto.spec.SecretKeySpec;/**
 * 加密和解密的示例代码
 */public class Main{    private static final String ALGORITHM = "AES";    private static final String defaultCharset = "UTF-8";    private static final String KEY_GCM_AES = "AES/GCM/NoPadding";    private static final int AES_KEY_SIZE = 128;    /**
     * 生成length字节的偏移量IV
     * createIV的功能<br>
     *
     * @param length
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static String createIV(int length) throws NoSuchAlgorithmException {
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");        byte[] salt = new byte[length];
        random.nextBytes(salt);        return encodeToBase64(salt);
    }    /**
     * 解密——使用自定义的加密key
     *
     * @param data
     * @param key
     * @param ivStr
     * @return
     * @throws Exception
     */
    public static String decryptByGcm(String data, String key, String ivStr) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_GCM_AES);        byte[] iv = decodeFromBase64(ivStr);
        SecretKeySpec keySpec = getSecretKeySpec(key);
        cipher.init(2, keySpec, new GCMParameterSpec(AES_KEY_SIZE, iv));        byte[] content = decodeFromBase64(data);        byte[] result = cipher.doFinal(content);        return new String(result);
    }    /**
     * 加密——使用自定义的加密key
     *
     * @param data
     * @param key
     * @param ivStr
     * @return
     * @throws Exception
     */
    public static String encryptByGcm(String data, String key, String ivStr) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_GCM_AES);        byte[] iv = decodeFromBase64(ivStr);
        SecretKeySpec keySpec = getSecretKeySpec(key);
        cipher.init(1, keySpec, new GCMParameterSpec(AES_KEY_SIZE, iv));        byte[] content = data.getBytes(defaultCharset);        byte[] result = cipher.doFinal(content);        return encodeToBase64(result);
    }    private static byte[] decodeFromBase64(String data) {        return Base64.getDecoder().decode(data);
    }    private static String encodeToBase64(byte[] data) {        return Base64.getEncoder().encodeToString(data);
    }    /**
     * 公共使用,获取SecretKeySpec
     *
     * @param key
     * @return
     */
    private static SecretKeySpec getSecretKeySpec(String key) {
        SecretKeySpec keySpec = null;        try {            //1.构造密钥生成器,指定为AES算法,不区分大小写
            KeyGenerator kgen = KeyGenerator.getInstance(ALGORITHM);            //2.根据ecnodeRules规则初始化密钥生成器
            //生成一个128位的随机源,根据传入的字节数组
            SecureRandom secureRandom= SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(key.getBytes(defaultCharset));
            kgen.init(AES_KEY_SIZE, secureRandom);            //3.产生原始对称密钥
            SecretKey secretKey = kgen.generateKey();            //4.获得原始对称密钥的字节数组
            byte[] enCodeFormat = secretKey.getEncoded();            //5.根据字节数组生成AES密钥
            keySpec = new SecretKeySpec(enCodeFormat, ALGORITHM);
        } catch (Exception e) {
            System.out.println("To do get SecretKeySpec exception!");
        }        return keySpec;
    }    public static void main(String[] args) throws Exception {
        String secret = "8cf860c0-30b7-4357-a104-fa627c59085d";//app的secret,加解密使用,开放平台获得
        String reqJson="{\"eventType\":\"corpAuth\",\"tenantId\":\"tenant\",\"timestamp\":\"1565167553\"}";
        String respJson="{\"timestamp\":\"1565167553\",\"msg\":\"success\"}";
        System.out.println("[请求Json]:"+reqJson);
        String ivStr = createIV(16);
        String reqStr=encryptByGcm(reqJson,secret, ivStr);
        System.out.println("[请求密文]:" + ivStr + reqStr);
        String reqJsonAfterDecrypt=decryptByGcm(reqStr, secret, ivStr);
        System.out.println("[请求密文解析后]:"+reqJsonAfterDecrypt);
        String ivStr2 = createIV(16);
        System.out.println("[响应Json]:"+respJson);
        String respStr=encryptByGcm(respJson,secret, ivStr2);
        System.out.println("[响应密文]:" + ivStr2 + respStr);
        String respJsonAfterDecrypt=decryptByGcm(respStr,secret, ivStr2);
        System.out.println("[响应密文解析后]:"+respJsonAfterDecrypt);
    }
}/*
[请求Json]:{"eventType":"corpAuth","tenantId":"tenant","timestamp":"1565167553"}
[请求密文]:PGkTPQrrTwlqBEu5pzPyxw==3BWfWmYTj67h5qdD4og6el7GrxaXHqm0gndcv/X8zK6j9ablMO+571LbjQWJJogcIunLPkJf9Yo4iHAP+QIB3KcihrLj3IHrRhbE8KuQvzCPVAo=
[请求密文解析后]:{"eventType":"corpAuth","tenantId":"tenant","timestamp":"1565167553"}
[响应Json]:{"timestamp":"1565167553","msg":"success"}
[响应密文]:5wwd5oVCbwgvaGzE2W9vPg==kdG1FYbicMlNY77ALZdBtC1ylS0aF+jzff8iyq2Ro1SJqUQCTAG96hLp+A7OyX/Im8IoFQ1XtfE=
[响应密文解析后]:{"timestamp":"1565167553","msg":"success"}
*/


【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。