Przeglądaj źródła

微信会话内容存储开发

anderx 6 miesięcy temu
rodzic
commit
b7e89a9c56

+ 15 - 0
pom.xml

@@ -374,6 +374,21 @@
 			<artifactId>thumbnailator</artifactId>
 			<version>0.4.8</version>
 		</dependency>
+		<dependency>
+			<groupId>org.bouncycastle</groupId>
+			<artifactId>bcprov-jdk15on</artifactId>
+			<version>1.64</version>
+		</dependency>
+		<dependency>
+			<groupId>org.bouncycastle</groupId>
+			<artifactId>bcpg-jdk16</artifactId>
+			<version>1.46</version>
+		</dependency>
+		<dependency>
+			<groupId>org.bouncycastle</groupId>
+			<artifactId>bcpkix-jdk15on</artifactId>
+			<version>1.64</version>
+		</dependency>
 
 	</dependencies>
 	<build>

+ 46 - 0
src/main/java/com/kede/common/utils/RSAEncrypt.java

@@ -0,0 +1,46 @@
+package com.kede.common.utils;
+
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+
+import javax.crypto.Cipher;
+import java.io.Reader;
+import java.io.StringReader;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.spec.RSAPrivateKeySpec;
+import java.util.Base64;
+
+public class RSAEncrypt {
+
+    public static String decryptRSA(String str, String privateKey) throws Exception {
+        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+        Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
+        rsa.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKey));
+        byte[] utf8 = rsa.doFinal(Base64.getDecoder().decode(str));
+        String result = new String(utf8,"UTF-8");
+        return result;
+    }
+
+    public static Key getPrivateKey (String privateKey) throws Exception {
+        String privKeyPEMNew = privateKey.replaceAll("\\n", "")
+                .replace("-----BEGIN PRIVATE KEY-----", "")
+                .replace("-----END PRIVATE KEY-----", "")
+                .replaceAll(" ", "");
+        byte[] e = Base64.getDecoder().decode(privKeyPEMNew);
+        PrivateKeyInfo pki = PrivateKeyInfo.getInstance(e);
+        RSAPrivateKey pkcs1Key = RSAPrivateKey.getInstance(pki.parsePrivateKey());
+        RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(pkcs1Key.getModulus(),pkcs1Key.getPrivateExponent());
+        // 实例化KeyFactory对象,并指定 RSA 算法
+        KeyFactory rsa = KeyFactory.getInstance("RSA");
+        // 获得 PrivateKey 对象
+        PrivateKey realprivateKey = rsa.generatePrivate(rsaPrivateKeySpec);
+        return realprivateKey;
+    }
+
+}

+ 31 - 0
src/main/java/com/kede/wechat/controller/ConversationContentController.java

@@ -0,0 +1,31 @@
+package com.kede.wechat.controller;
+
+
+import com.kede.common.controller.BaseController;
+import com.kede.wechat.service.ConversationContentService;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@RestController
+@RequestMapping(value = "/api/admin/release")
+public class ConversationContentController extends BaseController {
+
+    @Resource
+    private ConversationContentService conversationContentService;
+
+    /**
+     * 获取会话内容
+     * @return
+     */
+    @RequestMapping(value = "/getChatData", method = RequestMethod.GET)
+    public List getChatData()
+    {
+    	return conversationContentService.getChatData();
+    }
+
+
+}

+ 7 - 0
src/main/java/com/kede/wechat/service/ConversationContentService.java

@@ -0,0 +1,7 @@
+package com.kede.wechat.service;
+
+import java.util.List;
+
+public interface ConversationContentService {
+    List getChatData();
+}

+ 117 - 0
src/main/java/com/kede/wechat/service/impl/ConversationContentServiceImpl.java

@@ -0,0 +1,117 @@
+package com.kede.wechat.service.impl;
+
+import com.kede.common.utils.RSAEncrypt;
+import com.kede.wechat.service.ConversationContentService;
+import com.tencent.wework.Finance;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service("ConversationContentService")
+public class ConversationContentServiceImpl implements ConversationContentService {
+
+    @Value(value = "${conversationContent.corpid}")
+    private String corpid;
+    @Value(value = "${conversationContent.secret}")
+    private String secret;
+
+    static String priKey ="-----BEGIN PRIVATE KEY-----\n" +
+            "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCnh5N/kiyeNx/y\n" +
+            "dDKe3FO8HWj8tuQQUCkCc8ol1Sqmcs9ey7S2189UDbZOPeY2xiiMD6AdFVUK5Y0P\n" +
+            "gbX/XZFVizhwXseE+1IV1k0w3C++5Y4Zll7nZoJ05ANMbj+dlamAlNYOutmc0lZf\n" +
+            "CGXM5oeu+U0XDWE1+uY7TRAGV3OcA7GBraXiothncbkNf0ZwfBEGVyLNHcNODgwz\n" +
+            "ECc0KJuEF2V+UAt11HloT60atSmdiktwbJ6KOJ62TM/eU+F/5d3UJLdw0Xb8kEFn\n" +
+            "PYK/1mD8PGKFA6F0wSajNLc6iPg5mgiOS5j02N2+nEN8m7vIdL7Hd0yrigf5kEdl\n" +
+            "x9PC7OYpAgMBAAECggEAIUhSCVok+xnAgw6Kr/zdtdWx8eLz1DnYq+ANHQ8IdiEB\n" +
+            "Cbg2osylJ/HBcjHh9qfPjmPcT2rZhlysdZQzcBRKt0/9WnW59bVoTWZR148m/Snl\n" +
+            "3Td+i0JWUJqr/Rc6FDiTgDQRgsOkT/+JoT+t2dNCyYRFyHqkAXFjJAzfTiM5mJjg\n" +
+            "h8q7bU/LXoc49kuaNYmt5rV1nACoIh7MmKZpmR4OKXDWrl52Xt5qCEonp3bAH/S3\n" +
+            "HaTqsoqot7fF5aZWEuDWUeYQPzgVSsdw2k9Lq1c8aoE15IRfQCxtBOYpk8LEDJty\n" +
+            "JIGHrzBEHP7cok4UdBWbfuI1YjqmjefQovQumKqfcQKBgQDlXDrFFvx+k9pUI2bo\n" +
+            "KXcqroOcpdsssO5uJOG1PwuM02wIR0hP1d89fItFwLLuzjb+jeyN3qwFgoh21fkk\n" +
+            "Z3HkxubMY4fpXk1mAsTPUZxll8YYgGE7tXk1Rk5xsBMzlFRW88N7Ww/rQMBOlpJA\n" +
+            "6UfZGFYMezb1vXf7u3M4w/N22wKBgQC6/OF5DsCHAOui8CPXS2z2YTKuoOJmHKIe\n" +
+            "vBeXy+ic+YRuTBXgA0Z1wL4xDW/LeCqoTmetbTuvGSyahMV4QSK0pxH9gk80sH4+\n" +
+            "YEza3DLASxr4gSsr2fL6siuXN/OHyMClQb6JVa0yuPG1zLzShaIXYVAyz1m9rU1B\n" +
+            "JpQfbEV8SwKBgGnBDvAVWzLZ5Y9qC2b8x4+Mi+7MorSGmEl9A/1YJnE2gteGVpdJ\n" +
+            "G/fz1xvAcECgIMdalToM8/BOcbGpk4jvkZpVA9I2ClLWQoXIvQqCrMRDKPnfJ/Yq\n" +
+            "BMtFsaHF9/SObhzxpK7aHAry/ji+QI1hgtlc2UBEB+FjDVGq6sO/xiCFAoGAV/3Y\n" +
+            "PJdskhskx7AprY6Y3/gtWW/sGhEvWTc6Avy5oeTpJN45DQl9hrwExCCGk0EhEcKT\n" +
+            "l1Dn3SYDj1MbedNs+YYFOT6w9yJ2xfObNwKX/1VYg8iFAEW3ybE+QhLaTvNxdFLT\n" +
+            "zty6fDv7wolt8cNg24+3pbFdkZyT1izJV2Aj8rECgYBZQMObUgJ57n8FrXCdcdSa\n" +
+            "MGsFYl8KUZa1zvzhoi3h7YtbVNNtSHcEK7VDyIheBt9pFCN7nuDX3jW2XYW2USEC\n" +
+            "T7Pab2DAXPkRmXspWNID21G+AqqGSLD4pk9+ZmqevTfAtI+ajiu5NH1guH73rVOM\n" +
+            "xGjQ5dCmWpUGt4fmO+zWIw==\n" +
+            "-----END PRIVATE KEY-----";
+    static String  pubKey ="-----BEGIN PUBLIC KEY-----\n" +
+            "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp4eTf5Isnjcf8nQyntxT\n" +
+            "vB1o/LbkEFApAnPKJdUqpnLPXsu0ttfPVA22Tj3mNsYojA+gHRVVCuWND4G1/12R\n" +
+            "VYs4cF7HhPtSFdZNMNwvvuWOGZZe52aCdOQDTG4/nZWpgJTWDrrZnNJWXwhlzOaH\n" +
+            "rvlNFw1hNfrmO00QBldznAOxga2l4qLYZ3G5DX9GcHwRBlcizR3DTg4MMxAnNCib\n" +
+            "hBdlflALddR5aE+tGrUpnYpLcGyeijietkzP3lPhf+Xd1CS3cNF2/JBBZz2Cv9Zg\n" +
+            "/DxihQOhdMEmozS3Ooj4OZoIjkuY9NjdvpxDfJu7yHS+x3dMq4oH+ZBHZcfTwuzm\n" +
+            "KQIDAQAB\n" +
+            "-----END PUBLIC KEY-----";
+    @Override
+    public List<String>  getChatData() {
+        String message = null;
+        List<String> list = new ArrayList<>();
+        long sdk = Finance.NewSdk();
+        System.out.println(Finance.Init(sdk, corpid, secret));
+        long ret = 0;
+        int seq = 0;
+        int limit = 20;
+        long slice = Finance.NewSlice();
+        ret = Finance.GetChatData(sdk, seq, limit, null, null, 3, slice);
+        if (ret != 0) {
+            System.out.println("getchatdata ret " + ret);
+
+        }
+        String getchatdata = Finance.GetContentFromSlice(slice);
+        JSONObject jo = new JSONObject(getchatdata);
+        JSONArray chatdata = jo.getJSONArray("chatdata");
+
+        System.out.println("消息数:" + chatdata.length());
+        for (int i = 0; i < chatdata.length(); i++) {
+            String item = chatdata.get(i).toString();
+//            item:{
+//            "publickey_ver":1,
+//            "encrypt_chat_msg":"XSeHr+qM0YfX/q5WWvmnVEuL2E4U0RclkIbK9JnWQq7Y/0Ynir3srKh+9DH6BqRj/NYXKmUC65+0WRo
+    //            QboW3dC200XTqVNj+z0kljl11HdNmjuS3p7g0L+DYG5Bdz6Lo8FuHf/5WkOVi/R5VxOO6FvU+PediEOSCBN2dVHCeyIvExCk5mh
+    //            o+ME9QV/Q19HujmHpxzl3cu1XvBAZ7YvIpd2d/BCCI9JwmBc5bXr2FQ0r/PFkZadKGjw0cxqPyNIBkNc+ic0wHhWSZFTvY3qDW2
+    //            FFAdkYW68w8PA5f+8gD9VUuxKzz/j3Fu/6547Ayyuo93lPtbud5lAWXyeLexyHujFmdr300MA44BoYZsuTqdVsHbtEGLw967kO1
+//                oHcImidD416ZuQh2VZiOYTJMtDZALdB1tXramb7kFj0AsKloFLKeg3ks3TYRKwEmM9wJ/y2hGDJzq5xncBFFShbLao5u",
+//            "msgid":"1634016854685227651_1749628622146_external",
+//            "encrypt_random_key":"kJ3XJM7CB7DGZqFxSfT8+EDWfr8vZmVVKzKLl5FM47Sd7iUSlgZF/a4dm5sTpCMW3uzk/m1LZV4odH
+//                  RyVO2+AaVJsidMoLV2LmmWiCAuXkzdK4yDbGItkvpZGrilgAlJjR8EhsUuqXOjvxkUemGvKMhrUqKdCaO4p3GYT/cxMRJDUmrP0v
+//                  TQqdgwznQOSubbBFDdMTSAfEtZnGp5tOQdCNFCbIqks4/Y8Isd/nd/Kq3QNsXjE+GdZkqVuOqRujOeCeqFRJF022i2KFuJQ+R+7X
+//                  1es92NKBbaNT1AlRNfjtACH1KrPRLlEnmAS0EUIWWmn45/kOpH3T4ovOuNDnrafw==",
+//            "seq":1}
+            JSONObject data = new JSONObject(item);
+            String encrypt_random_key = data.getString("encrypt_random_key");
+            String encrypt_chat_msg = data.getString("encrypt_chat_msg");
+            long msg = Finance.NewSlice();
+            try {
+                 message = RSAEncrypt.decryptRSA(encrypt_random_key, priKey);
+                ret = Finance.DecryptData(sdk, message, encrypt_chat_msg, msg);
+                if (ret != 0) {
+                    System.out.println("getchatdata ret " + ret);
+//                    return;
+                }
+                String str = String.valueOf(Finance.GetContentFromSlice(msg));
+                System.out.println("decrypt ret:" + ret + " msg:" + str);
+                Finance.FreeSlice(msg);
+                list.add(str);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+        }
+        Finance.FreeSlice(slice);
+    return list;
+    }
+}

+ 156 - 0
src/main/java/com/tencent/wework/Finance.java

@@ -0,0 +1,156 @@
+package com.tencent.wework;
+
+/* sdk返回数据
+typedef struct Slice_t {
+    char* buf;
+    int len;
+} Slice_t;
+
+typedef struct MediaData {
+    char* outindexbuf;
+    int out_len;
+    char* data;
+    int data_len;
+    int is_finish;
+} MediaData_t;
+*/
+
+import org.apache.commons.io.IOUtils;
+import org.springframework.core.io.ClassPathResource;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+
+
+public class Finance {
+    public native static long NewSdk();
+
+    /**
+     * 初始化函数
+     * Return值=0表示该API调用成功
+     *
+     * @param   sdk			NewSdk返回的sdk指针
+     * @param   corpid      调用企业的企业id,例如:wwd08c8exxxx5ab44d,可以在企业微信管理端--我的企业--企业信息查看
+     * @param   secret		聊天内容存档的Secret,可以在企业微信管理端--管理工具--聊天内容存档查看
+     *
+     *
+     * @return 返回是否初始化成功
+     *      0   - 成功
+     *      !=0 - 失败
+     */
+    public native static int Init(long sdk, String corpid, String secret);
+
+    /**
+     * 拉取聊天记录函数
+     * Return值=0表示该API调用成功
+     *
+     *
+     * @param   sdk				NewSdk返回的sdk指针
+     * @param   seq				从指定的seq开始拉取消息,注意的是返回的消息从seq+1开始返回,seq为之前接口返回的最大seq值。首次使用请使用seq:0
+     * @param   limit			一次拉取的消息条数,最大值1000条,超过1000条会返回错误
+     * @param   proxy			使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
+     * @param   passwd			代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
+     * @param  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   sdk				NewSdk返回的sdk指针
+     * @param   sdkFileid		从GetChatData返回的聊天消息中,媒体消息包括的sdkfileid
+     * @param   proxy			使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
+     * @param   passwd			代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
+     * @param   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   encrypt_key, getchatdata返回的encrypt_key
+     * @param   encrypt_msg, getchatdata返回的content
+     * @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 {
+        String osname = System.getProperties().getProperty("os.name");
+        if (osname.contains("Windows")) {
+            System.out.println("系统为Windows");
+            //windows下运行需要给资源文件夹下四个注册表存放在下面打印的目录任何一个地方即可,linux直接打包丢上去运行即可
+//            System.out.println(System.getProperty("java.library.path"));
+//            System.loadLibrary("WeWorkFinanceSdk");
+            String path="";
+            System.load(path.concat("C:\\gitProject\\kede-officialWeb-HN\\src\\main\\resources\\lib\\libcrypto-1_1-x64.dll"));
+            System.load(path.concat("C:\\gitProject\\kede-officialWeb-HN\\src\\main\\resources\\lib\\libcurl-x64.dll"));
+            System.load(path.concat("C:\\gitProject\\kede-officialWeb-HN\\src\\main\\resources\\lib\\libssl-1_1-x64.dll"));
+            System.load(path.concat("C:\\gitProject\\kede-officialWeb-HN\\src\\main\\resources\\lib\\WeWorkFinanceSdk.dll"));
+        } else if (osname.contains("Linux")) {
+            System.out.println("系统为Linux");
+            System.load(" /usr/local/tomcat-kede/webapps/test/WEB-INF/classes/jniLibs/libWeWorkFinanceSdk_Java.so");
+//            System.loadLibrary("WeWorkFinanceSdk");
+        } else {
+            System.out.println(String.format("不兼容的系统:{%s}, osname"));
+        }
+    }
+
+
+
+}

BIN
src/main/resources/jniLibs/libWeWorkFinanceSdk_Java.so


BIN
src/main/resources/lib/WeWorkFinanceSdk.dll


BIN
src/main/resources/lib/libcrypto-1_1-x64.dll


BIN
src/main/resources/lib/libcurl-x64.dll


BIN
src/main/resources/lib/libssl-1_1-x64.dll


+ 4 - 1
src/main/resources/props/config_local.properties

@@ -18,6 +18,9 @@ static.host=//www.kedexinxi.com/client/1.0.13
 #static.host=//172.16.0.255:3000/kede_ow/client/1.0.9
 
 
+conversationContent.corpid=wwd1f2fb82952be74c
+conversationContent.secret=0k4XYuHUl4QB8QTB6cAOSaRIX00ur1Vyjjz4p_REP_k
+
 #\u68c0\u6d4b\u6570\u636e\u5e93\u94fe\u63a5\u662f\u5426\u6709\u6548\uff0c\u5fc5\u987b\u914d\u7f6e
 jdbc.validationQuery=SELECT 'x'
 #\u521d\u59cb\u8fde\u63a5\u6570
@@ -50,7 +53,7 @@ jdbc.logAbandoned=true
 #\u7edf\u8ba1\u76d1\u63a7
 jdbc.filters=stat
 
-logging.level.com.kede=DEBUG
+logging.level.com=DEBUG
 
 jedis.host=localhost
 jedis.port=6379

+ 3 - 0
src/main/resources/props/config_prod.properties

@@ -82,6 +82,9 @@ mobileCodeTemplate=SMS_37845022
 mobileRemindCodeTemplate=SMS_137421952
 patentTemplate=SMS_72005286
 
+conversationContent.corpid=wwd1f2fb82952be74c
+conversationContent.secret=0k4XYuHUl4QB8QTB6cAOSaRIX00ur1Vyjjz4p_REP_k
+
 easemob.client.url=https://a1.easemob.com/1117170814115609/jitao
 easemob.client.id=YXA6RUDGkIDSEeemq9_4PWzcNA
 easemob.client.secret=YXA61NHE0renI5N8gEgizoI5ivM9SYE

+ 2 - 1
src/main/resources/spring/spring-mvc.xml

@@ -15,7 +15,8 @@
 	<!-- Scan controller -->
 
 	<context:component-scan
-		base-package="com.kede.*.controller;com.kede.*.*.controller;com.kede.common.task;com.kede.core.websocket" />
+		base-package="com.kede.*.controller;com.kede.*.*.controller;com.kede.common.task;com.kede.core.websocket;
+			com.tencent.wework" />
 
 	<bean name="springContextUtils" class="com.kede.common.utils.SpringContextUtils" scope="singleton"></bean>