简单介绍一下腾讯会议Webhook与REST API的差异,调用方式如下图:
可以看到,REST API是由用户应用主动向腾讯会议后台发起请求,然后后台进行响应;而Webhook是由特定事件来触发,然后腾讯Webhook服务主动向用户发起请求。例如在配置Webhook时订阅了用户入会事件消息之后,当会议中有人入会时,后台就会给用户配置的Webhook应用发送POST消息。
参考官网文档进行开通:https://cloud.tencent.com/document/product/1095/51605
Webhook消息分为GET和POST消息两类,当配置事件消息订阅和后台发送心跳消息时,此时请求的类型是GET消息;当订阅的会议事件被触发时,后台会发送POST类型的消息。
GET消息的处理流程如下:
POST消息的处理流程如下:
保存Webhook配置或者收到心跳消息时打印check_str解码后的结果
会议事件触发时打印收到的POST消息内容
在保存配置时可能会遇到下面的报错提示:
出现这个报错是因为后台给配置的URL发送了一个GET消息,但是没有收到正确的响应。遇到这个问题先确认有没有收到GET消息,如果没有收到,说明配置的URL不对或者消息被拦截了。先确认服务是否已经在配置的URL启动成功了,然后确认防火墙规则是否已经放开Webhook访问,防火墙规则参考这里:https://meeting.tencent.com/support/topic/1929/。如果已经收到GET消息仍然报错,需要确认是否有正确响应处理,具体可以参考接入文档或下面的代码。
WebhookServer.java
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;
public class WebhookServer {
public static void main(String[] args) throws IOException {
HttpServer httpServer = HttpServer.create(new InetSocketAddress(2306), 10);
httpServer.createContext("/java_webhook", new WebhookHandler());
httpServer.start();
}
}
WebhookHandler.java
import com.google.gson.Gson;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class WebhookHandler implements HttpHandler {
public void handle (HttpExchange exchange) {
String method = exchange.getRequestMethod();
System.out.println("method: " + method);
if (method.contains("GET")) {
doGet(exchange);
} else if (method.contains("POST")) {
doPost(exchange);
} else {
System.out.println("method: " + method + "is illegal");
}
}
private static Map<String,String> formData2Dic(String formData ) {
Map<String,String> result = new HashMap<>();
if(formData== null || formData.trim().length() == 0) {
return result;
}
final String[] items = formData.split("&");
Arrays.stream(items).forEach(item ->{
final String[] keyAndVal = item.split("=");
if( keyAndVal.length == 2) {
try{
final String key = URLDecoder.decode( keyAndVal[0],"utf8");
final String val = URLDecoder.decode( keyAndVal[1],"utf8");
result.put(key,val);
}catch (UnsupportedEncodingException e) {}
}
});
return result;
}
private void doGet(HttpExchange exchange) {
String queryString = exchange.getRequestURI().getQuery();
Map<String, String> queryStringInfo = formData2Dic(queryString);
String checkStr = queryStringInfo.get("check_str");
Base64.Decoder decoder = Base64.getDecoder();
String checkData = new String(decoder.decode(checkStr));
// 由于获取checkStr时会把Base64默认补齐的"="去掉,这里再转一次补回来
Base64.Encoder encoder = Base64.getEncoder();
checkStr = encoder.encodeToString(checkData.getBytes());
//System.out.println("check_str:" + checkStr + " checkData:" + checkData);
String timestamp = exchange.getRequestHeaders().get("timestamp").get(0);
String nonce = exchange.getRequestHeaders().get("nonce").get(0);
String signature = exchange.getRequestHeaders().get("signature").get(0);
//System.out.println("timestamp:" + timestamp + " nonce:" + nonce + " signature:" + signature);
String checkSignature = Sha1Util.calSignature(WebhookConfig.token, timestamp, nonce, checkStr);
//System.out.println("checkSignature:" + checkSignature);
if (checkSignature.contentEquals(signature)) {
try {
// 验证通过时需要返回base64解码后的check_str参数,并且HTTP 头部响应200
System.out.println(checkData);
exchange.sendResponseHeaders(200, checkData.length());
OutputStream os = exchange.getResponseBody();
os.write(checkData.getBytes());
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
try {
// 验证不通过时报错
String response = "check signature failed";
System.out.println(response);
exchange.sendResponseHeaders(400, response.length());
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private void doPost(HttpExchange exchange) {
String timestamp = exchange.getRequestHeaders().get("timestamp").get(0);
String nonce = exchange.getRequestHeaders().get("nonce").get(0);
String signature = exchange.getRequestHeaders().get("signature").get(0);
String postBody = null;
PostMsg rawData = null;
String msg = null;
//System.out.println("timestamp:" + timestamp + " nonce:" + nonce + " signature:" + signature);
try {
postBody = IOUtils.toString(exchange.getRequestBody(), "UTF-8");
//System.out.println(postBody + "\n");
Gson gson = new Gson();
rawData = gson.fromJson(postBody, PostMsg.class);
//System.out.println(rawData.data);
Base64.Decoder decoder = Base64.getDecoder();
msg = new String(decoder.decode(rawData.data));
//System.out.println(msg);
} catch (Exception e) {
System.out.println(e);
throw new RuntimeException(e);
}
String checkSignature = Sha1Util.calSignature(WebhookConfig.token, timestamp, nonce, rawData.data);
//System.out.println("checkSignature:" + checkSignature);
if (checkSignature.contentEquals(signature)) {
////////////
//对post消息进行处理,建议异步处理,防止超时
System.out.println(msg);
////////////
try {
// 验证通过时固定的字符串,并且HTTP 头部响应200
String response = "successfully received callback";
System.out.println(response);
exchange.sendResponseHeaders(200, response.length());
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
try {
// 验证不通过时报错
String response = "check signature failed";
System.out.println(response);
exchange.sendResponseHeaders(400, response.length());
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
Sha1Util.java
import java.security.MessageDigest;
import java.util.Arrays;
public class Sha1Util {
// 生成签名
public static String calSignature(String token, String timestamp, String nonce, String data) {
String[] arr = new String[]{token, timestamp, nonce, data};
Arrays.sort(arr);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
}
return getSha1(sb.toString());
}
// sha1签名算法
public static String getSha1(String str) {
if (str == null || str.length() == 0) {
return null;
}
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
try {
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char buf[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
} catch (Exception e) {
return null;
}
}
}
PostMsg.java
public class PostMsg {
String data;
}
WebhookConfig.java
public class WebhookConfig {
public static final String token = "xzXxPIag24PDBSoZqJsfXBq9E";
}
源码下载:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。