Welink接口回调及事件列表
【摘要】 接口回调1、填写回调地址填写回调地址,例如:https://example.com/callback。 为了安全,强烈推荐使用https。2、验证回调参数以及解密数据WeLink每一次访问回调URL的时候将发出如下请求(下面链接中的值只是示例,并不代表真实值):POST https://您服务端部署的IP:您的端口/callback# 包含的json数据如下,其中encrypt是经过加密的消...
接口回调
1、填写回调地址
填写回调地址,例如:https://example.com/callback。 为了安全,强烈推荐使用https。
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)