前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >腾讯会议API - Webhook介绍与简单实现

腾讯会议API - Webhook介绍与简单实现

原创
作者头像
liquid
修改2023-08-21 10:37:27
2.6K0
修改2023-08-21 10:37:27
举报
文章被收录于专栏:腾讯会议腾讯会议

什么是Webhook?

简单介绍一下腾讯会议Webhook与REST API的差异,调用方式如下图:

可以看到,REST API是由用户应用主动向腾讯会议后台发起请求,然后后台进行响应;而Webhook是由特定事件来触发,然后腾讯Webhook服务主动向用户发起请求。例如在配置Webhook时订阅了用户入会事件消息之后,当会议中有人入会时,后台就会给用户配置的Webhook应用发送POST消息。

怎么开通配置?

参考官网文档进行开通:https://cloud.tencent.com/document/product/1095/51605

在接入Webhook之前开发者需要了解的信息

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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
public class PostMsg {
    String data;
}

WebhookConfig.java

代码语言:javascript
复制
public class WebhookConfig {
    public static final String token = "xzXxPIag24PDBSoZqJsfXBq9E";
}

源码下载:

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是Webhook?
  • 怎么开通配置?
  • 在接入Webhook之前开发者需要了解的信息
  • 实现效果
  • 可能会遇到的问题
  • 代码实现
相关产品与服务
腾讯会议
腾讯会议(Tencent Meeting)为企业打造专属的会议能力,卓越的音视频性能,丰富的会议协作能力,坚实的会议安全保障,提升协作效率,满足大中小会议全场景需求。您可以使用腾讯会议进行远程音视频会议、在线协作、会管会控、会议录制、指定邀请、布局管理、同声传译等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档