企业微信-会话内容存档(从零开始,完整demo)

1、企业微信-会话内容存档-配置服务器

a.接收事件服务器配置

        1)token、sEncodingAESKey   随机获取即可,保存好下面会用到;

        2)url,需要后台部署服务,外网可以访问,接口如下;

/**
     * 验证回调URL
     * 企业开启回调模式时,企业微信会向验证url发送一个get请求
     * 假设点击验证时,企业收到类似请求:
     * * GET /cgi-bin/wxpush?msg_signature=5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3&timestamp=1409659589&nonce=263014780&echostr=P9nAzCzyDtyTWESHep1vC5X9xho%2FqYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp%2B4RPcs8TgAE7OaBO%2BFZXvnaqQ%3D%3D
     * * HTTP/1.1 Host: qy.weixin.qq.com
     * <p>
     * 接收到该请求时,企业应		1.解析出Get请求的参数,包括消息体签名(msg_signature),时间戳(timestamp),随机数字串(nonce)以及企业微信推送过来的随机加密字符串(echostr),
     * 这一步注意作URL解码。
     * 2.验证消息体签名的正确性
     * 3. 解密出echostr原文,将原文当作Get请求的response,返回给企业微信
     * 第2,3步可以用企业微信提供的库函数VerifyURL来实现。
     */
    @Override
    @GetMapping("weChatPush")
    public String weChatPush() throws AesException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//        String sToken = "QDG6xxx";
        String sToken = enterpriseWechatConfig.getQyChatToken();
//        String sCorpID = "wx5823bf9xxxxxxxx";
        String sCorpID = enterpriseWechatConfig.getQyCorpid();
//        String sEncodingAESKey = "jWmYm7qr5nMoAUwZRjGtBxmz3KA1txxxxxxxxxxx";
        String sEncodingAESKey = enterpriseWechatConfig.getQyChatEncodingAESKey();
        log.info("获取 diamond 配置 sToken:{}  sCorpID:{}   sEncodingAESKey:{}", sToken, sCorpID, sEncodingAESKey);

        WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(sToken, sEncodingAESKey, sCorpID);
        // 解析出url上的参数值如下:
        String sVerifyMsgSig = request.getParameter("msg_signature");
        String sVerifyTimeStamp = request.getParameter("timestamp");
        String sVerifyNonce = request.getParameter("nonce");
        String sVerifyEchoStr = request.getParameter("echostr");
        //需要返回的明文
        String sEchoStr = null;
        log.info("获取 url 参数 sVerifyMsgSig:{}  sVerifyTimeStamp:{}   sVerifyNonce:{}  sVerifyEchoStr:{}",
                sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce, sVerifyEchoStr);
        try {
            sEchoStr = wxcpt.VerifyURL(sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce, sVerifyEchoStr);
            log.info("返回的明文: {}", sEchoStr);
            return sEchoStr;
        } catch (Exception e) {
            //验证URL失败,错误原因请查看异常
            log.info("验证URL失败,错误原因请查看异常e:{}", e);
        }
        return sEchoStr;
    }

b.接收事件服务器配置

        可能出现问题:ip不可信

        解决方案:请删掉配置的ip,不要设置ip!        不要设置ip!        不要设置ip!

c.配置公钥

        1)生成密钥对(RSA,2048,PKCS#1),保存好公钥和私钥,后面会用到

                最简单的方法:http://web.chacuo.net/netrsakeypair

        2)将公钥填写到企业微信后台

                保存公钥后可以查看到【公钥版本 1】,【管理凭证密钥 secret】 这个后面会用到

2、企业微信-会话内容存档-处理消息(以linux为例)

        企业微信官方文档:获取会话内容 - 接口文档 - 企业微信开发者中心s​​​​​​

        企业微信官方文档,真的烂! 起码对我很不友好(可能我理解能力太差了吧,好多地方有问题)

a.引入依赖

        可能会出现的错误信息:「class "org.bouncycastle.openssl.PEMException"'s signer information does not match signer information of other classes in the same package」

        原因:bcpg-jdk16 中的 bcprov-jdk16 与  bcpkix-jdk15on 中的 bcprov-jdk15on 重复

        解决方案:需要排除 bcprov-jdk16,否则会报, 代码如下

        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpg-jdk16</artifactId>
            <version>1.46</version>
            <exclusions>
                <exclusion>
                    <groupId>org.bouncycastle</groupId>
                    <artifactId>bcprov-jdk16</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.64</version>
        </dependency>

b.下载sdk,将sdk微信提供的几个类放到项目中,注意包名要相同

Finance文件乱码,我这里稍微修改了一下

package com.tencent.wework;


/**
 * 企业微信会话sdk
 *   官网文档字符集有问题,注释有找到补充一下
 * @Author: hyl
 * @Date: 2022/2/25
 */
public class Finance {
	public native static long NewSdk();

	/**
	 * 初始化函数
	 * Return值=0表示该API调用成功
	 *
	 * @param [in]  sdk			NewSdk返回的sdk指针
	 * @param [in]  corpid      调用企业的企业id,例如:wwd08c8exxxx5ab44d,可以在企业微信管理端--我的企业--企业信息查看
	 * @param [in]  secret		聊天内容存档的Secret,可以在企业微信管理端--管理工具--聊天内容存档查看
	 *
	 * @return 返回是否初始化成功
	 *      0   - 成功
	 *      !=0 - 失败
	 */
	public native static int Init(long sdk, String corpid, String secret);

	/**
	 * 拉取聊天记录函数
	 * Return值=0表示该API调用成功
	 *
	 *
	 * @param [in]  sdk				NewSdk返回的sdk指针
	 * @param [in]  seq				从指定的seq开始拉取消息,注意的是返回的消息从seq+1开始返回,seq为之前接口返回的最大seq值。首次使用请使用seq:0
	 * @param [in]  limit			一次拉取的消息条数,最大值1000条,超过1000条会返回错误
	 * @param [in]  proxy			使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
	 * @param [in]  passwd			代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
	 * @param [out] chatDatas		返回本次拉取消息的数据,slice结构体.内容包括errcode/errmsg,以及每条消息内容。
	 *
	 * @return 返回是否调用成功
	 *      0   - 成功
	 *      !=0 - 失败
	 */
	public native static int GetChatData(long sdk, long seq, long limit, String proxy, String passwd, long timeout, long chatData);

	/**
	 * 拉取媒体消息函数
	 * Return值=0表示该API调用成功
	 *
	 *
	 * @param [in]  sdk				NewSdk返回的sdk指针
	 * @param [in]  sdkFileid		从GetChatData返回的聊天消息中,媒体消息包括的sdkfileid
	 * @param [in]  proxy			使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
	 * @param [in]  passwd			代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
	 * @param [in]  indexbuf		媒体消息分片拉取,需要填入每次拉取的索引信息。首次不需要填写,默认拉取512k,后续每次调用只需要将上次调用返回的outindexbuf填入即可。
	 * @param [out] media_data		返回本次拉取的媒体数据.MediaData结构体.内容包括data(数据内容)/outindexbuf(下次索引)/is_finish(拉取完成标记)

	 *
	 * @return 返回是否调用成功
	 *      0   - 成功
	 *      !=0 - 失败
	 */
	public native static int GetMediaData(long sdk, String indexbuf, String sdkField, String proxy, String passwd, long timeout, long mediaData);

	/**
	 * @brief 解析密文.企业微信自有解密内容
	 * @param [in]  encrypt_key, getchatdata返回的encrypt_random_key,使用企业自持对应版本秘钥RSA解密后的内容
	 * @param [in]  encrypt_msg, getchatdata返回的encrypt_chat_msg
	 * @param [out] msg, 解密的消息明文
	 * @return 返回是否调用成功
	 *      0   - 成功
	 *      !=0 - 失败
	 */
	public native static int DecryptData(long sdk, String encrypt_key, String encrypt_msg, long msg);

	public native static void DestroySdk(long sdk);
	public native static long NewSlice();
	/**
	 * @brief 释放slice,和NewSlice成对使用
	 * @return
	 */
	public native static void FreeSlice(long slice);

	/**
	 * @brief 获取slice内容
	 * @return 内容
	 */
	public native static String GetContentFromSlice(long slice);

	/**
	 * @brief 获取slice内容长度
	 * @return 内容
	 */
	public native static int GetSliceLen(long slice);
	public native static long NewMediaData();
	public native static void FreeMediaData(long mediaData);

	/**
	 * @brief 获取mediadata outindex
	 * @return outindex
	 */
	public native static String GetOutIndexBuf(long mediaData);
	/**
	 * @brief 获取mediadata data数据
	 * @return data
	 */
	public native static byte[] GetData(long mediaData);
	public native static int GetIndexLen(long mediaData);
	public native static int GetDataLen(long mediaData);

	/**
	 * @brief 判断mediadata是否结束
	 * @return 1完成、0未完成
	 */
	public native static int IsMediaDataFinish(long mediaData);

    static {
        System.loadLibrary("WeWorkFinanceSdk_Java");
    }
}

c.配置so文件

        官方提供了windows、linux两种环境,本人开发环境为mac系统,暂时未找到mac加载方法,只能本地开发,linux部署测试;

        方案1:so文件上传到指定目录,服务器启动加载外部so文件;

        1)将 libWeWorkFinanceSdk_Java.so 上传到 /home/solib 目录下(自己定义)

        2)linux环境启动项目时增加启动命令:-Djava.library.path=/home/solib  (如果配置到全局环境变量中也可以不增加启动命令)

        

        方案2:将so文件打包到项目中,服务器启动加载内部so文件;

        1)将so文件放到resources下,新建linux-x86-64文件夹内

         2)修改Finance类的静态代码块

static {
		try {
			String path = System.getProperty("java.io.tmpdir");
			String name = "libWeWorkFinanceSdk_Java.so";
			// 获取sources下的资源
			ClassPathResource classPathResource = new ClassPathResource("linux-x86-64/" + name);
			InputStream in = classPathResource.getInputStream();
			// 写入到临时文件
			FileUtil.writeStream(path + name, in);
        	System.load(path + name);
			log.info("{}so文件加载完成",path  + name );
		} catch (IOException e) {
			log.info("so文件加载识别:{}",e);
		}
    }

        

d.拉消息,并且解密

/**
     * 拉会话消息
     */
    @Override
    public void pullChat() {

        //使用sdk前需要初始化,初始化成功后的sdk可以一直使用。
        //如需并发调用sdk,建议每个线程持有一个sdk实例。
        log.info("加载企业微信sdk开始");
        long sdk = Finance.NewSdk();
        log.info("创建企业微信sdk成功");
        // 企业id
        String corpid = enterpriseWechatConfig.getQyCorpid();
        // 管理凭证密钥 配置完公钥后 可以获取
        String secret = enterpriseWechatConfig.getQyChatSecret();
        // 私钥,与公钥为一对
        String priKey = enterpriseWechatConfig.getQyChatPriKey();
        // 公钥版本号,判断消息能否解密
        String pubKeyVer = enterpriseWechatConfig.getQyChatPubKeyVer();

        log.info("读取配置文件  corpid:{}  secret:{}  priKey:{}", corpid, secret, priKey);
        Finance.Init(sdk, corpid, secret); // 初始化

        // seq 表示该企业存档消息序号,该序号单调递增,拉取序号建议设置为上次拉取返回结果中最大序号。
        // 首次拉取时seq传0,sdk会返回【有效期内】最早的消息。
        int seq = 0; // 从指定的seq开始拉取消息,注意的是返回的消息从seq+1开始返回,seq为之前接口返回的最大seq值。首次使用请使用seq:0(这个值需要记录下来,以便下一次的拉去)
        int limit = 1000;
        //创建切片
        long slice = Finance.NewSlice();
        try {
            //拉取聊天记录
            long ret = Finance.GetChatData(sdk, seq, limit, null, null, 5, slice);
            if (ret != 0) {
                log.info("拉取聊天记录失败 ret:{}", ret);
                return;
            }
            //获取切片中的内容
            String contentFromSlice = Finance.GetContentFromSlice(slice);
            log.info(seq + ",拉去的聊天记录密文结果:{}", contentFromSlice);// 测试完成后去掉
            JSONObject contentJsonObject = JSONObject.parseObject(contentFromSlice);
            //聊天内容
            JSONArray chatdata = contentJsonObject.getJSONArray("chatdata");
            for (int i = 0; i < chatdata.size(); i++) {
                log.info("开始循环处理,第{}条数据", i);
                JSONObject data = chatdata.getJSONObject(i);
                //公钥版本
                Integer publicKeyVer = data.getInteger("publickey_ver");
                if(ObjectUtil.notEqual(publicKeyVer,pubKeyVer)){
                    log.info("公钥版本不一致,无法解密当前消息,当前消息版本:{}   系统配置版本:{}",publicKeyVer,pubKeyVer);
                    continue;
                }
                //加密密钥
                String encryptRandomKey = data.getString("encrypt_random_key");
                //加密聊天消息
                String encryptChatMsg = data.getString("encrypt_chat_msg");
                long msg = Finance.NewSlice();
                try {
                    // 获取加密密钥
                    String encryptKey = RSAEncrypt.decryptRSA(encryptRandomKey, priKey);
                    log.info("解析密文.企业微信自有解密内容");
                    // 解析密文.企业微信自有解密内容
                    ret = Finance.DecryptData(sdk, encryptKey, encryptChatMsg, msg);
                    if (ret != 0) {
                        log.info("解密聊天记录失败 ret :{}", ret);
                        continue;
                    }
                    // 获取切片中的内容
                    String plaintext = Finance.GetContentFromSlice(msg);
                    log.info("解密结果:{}", plaintext);
                    // 释放slice
                    Finance.FreeSlice(msg);
                    JSONObject plaintextJson = JSONObject.parseObject(plaintext);
                    // 文件类型  "text"文本 ,"revoke"撤回消息
                    String msgtype = plaintextJson.getString("msgtype");
                    if (StrUtil.equals(msgtype, "text")) {
                        log.info("文本消息:{}", plaintextJson.getJSONObject("text").getString("content"));
                    } else {
                        log.info("其他消息");
                    }
                    log.info("会话内容写入数据库 ,存储消息,类型,时间,userid,等信息  :{}", plaintextJson);
                } catch (Exception e) {
                    log.error("循环拉会话异常:e:{}", e);
                }
            }
        } catch (Exception e) {
            log.error("拉会话消息异常:e:{}", e);
        } finally {
            // 释放slice
            Finance.FreeSlice(slice);
        }
    }

解密消息时,需要注意消息中 publickey_ver 字段和企业微信后台中版本号一致, 每次更新公钥后版本号都会+1,只有更改后发送的消息才会 使用新的版本号! 

私钥格式:

    String priKey = "-----BEGIN RSA PRIVATE KEY-----\n" +
            "MIICXAIBAAKBgQCLTqqYHxxxxx省略100字F32v5bfw3NzzwVHU\n" +
            "-----END RSA PRIVATE KEY-----";

  • 8
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 36
    评论
要搭建Vue3.0项目的环境,您可以按照以下步骤进行操作: 1. 首先,确保您已经安装了Node.js。您可以在命令行中输入 "node -v" 来检查是否已安装Node.js。如果*** 升级Vue CLI到4.0版本。如果您之前已经安装了旧版本的Vue CLI,需要先卸载旧版本。您可以在全局命令行中运行 "cnpm uni -g vue-cli" 来删除旧版本。然后,您可以在全局命令行中运行 "cnpm i -g @vue/cli" 来安装Vue CLI的4.0版本。最后,您可以通过运行 "vue -V" 检查Vue CLI的版本号,确保已经成功安装了4.0版本。 3. 创建一个新的Vue项目。您可以使用命令行进入到您想要创建项目的目录中,然后运行 "vue create 项目名" 来创建一个新的Vue项目。根据提示选择您所需的特性和插件,然后等待项目创建完成。 4. 进入项目文件夹。在命令行中切换到您刚刚创建的项目文件夹中,使用 "cd 项目名" 命令。 5. 启动项目。在项目文件夹中运行 "npm run serve" 命令来启动项目。这将启动一个开发服务器,并在浏览器中打开项目。 通过按照以上步骤,您就可以成功搭建Vue3.0项目的环境,并开始进行开发了。如果您需要更详细的配置或使用Vue3.0进行接口封装,以及使用ant-design-vue进行后台管理系统的开发,可以参考相应的文档或教程进行进一步学习。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [手把手教你vue3.0项目搭建](https://blog.csdn.net/weixin_42762167/article/details/112216178)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [配置一个vue3.0项目的完整步骤](https://download.csdn.net/download/weixin_38629130/13200273)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值