企业微信开发环境搭建
1. 应用的回调配置【验证有效性】
application.properties 配置文件
#企业id
qixin.qiYeId=*************
#应用回调Token
qixin.app.callbackToken=*************
#应用回调EncodingAESKey
qixin.app.callbackEncodingAESKey=****************************************************
java代码
import com.bhne.qixin.qixin.util.WXBizJsonMsgCrypt;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
@Value("${qixin.qiYeId}")
private String qiYeId;
@Value("${qixin.app.callbackToken}")
private String callbackToken;
@Value("${qixin.app.callbackEncodingAESKey}")
private String callbackEncodingAESKey;
/**
* @Description 企信Get回调接口
* @Param [msg_signature, nonce, echostr, timestamp][消息体签名, 随机数字串, 企业微信推送过来的随机加密字符串,时间戳]
* @Return java.lang.String
* @Author linxi
* @Date 2025/3/12 13:56
**/
@GetMapping(produces = "text/plain")
public String qxCallbackGet(@RequestParam String msg_signature, @RequestParam String nonce, @RequestParam String echostr, @RequestParam String timestamp) {
log.info("CallbackController.qxCallbackGet-->微信回调认证消息 msg_signature={}, nonce={}, echostr={}, timestamp={}",msg_signature, nonce, echostr, timestamp);
if (StringUtils.isAnyBlank(msg_signature, timestamp, nonce, echostr)) {
throw new IllegalArgumentException("CallbackController.qxCallbackGet-->请求参数非法,请核实!");
}
try {
//参数为 回调设置的Token 回调设置的Key 企业ID
WXBizJsonMsgCrypt wxcpt = new WXBizJsonMsgCrypt(callbackToken, callbackEncodingAESKey, qiYeId);
// 验证URL成功,将sEchoStr返回
return wxcpt.VerifyURL(msg_signature, timestamp, nonce, echostr);
} catch (Exception e) {
log.error("CallbackController.qxCallbackGet-->回调验证处理败", e);
}
return "";
}
pom.xml
<!-- 企信依赖 -->
<!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20231013</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.16.0</version>
</dependency>
注意
@GetMapping(produces = "text/plain") 中 produces = "text/plain" 是为了返回时候返回的为纯文本,否则会返回为json格式。企信需要返回纯文本,所以需要设置。
2.调用会话内容存档SDK
步骤
a. 把SDK v3.0中 libcrypto-3-x64.dll, libcurl-x64.dll, WeWorkFinanceSdk.dll 放到 C:\Windows\system32 路径下
b. 进入企信管理后台 高级功能 --> 会话内容存档
在 会话内容存档 中 选择服务范围, 可信IP地址[服务器地址], 消息加密公钥[RSA使用PKCS1模值为2048bit] 三项必填不然返回为空 c. 获取会话内容存档的 Secret d. 点击 开启
application.properties 配置文件
#企业id
qixin.qiYeId=*************
#会话存档secret
qixin.huihuacundang.secret=***************************************
#会话存档私钥
qixin.huihuacundang.privateKey=******************************************************************************************************************
java代码
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.tencent.wework.Finance;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.Cipher;
import java.util.Base64;
@Value("${qixin.qiYeId}")
private String qiYeId;
@Value("${qixin.huihuacundang.secret}")
private String hhcdSecret;
@Value("${qixin.huihuacundang.privateKey}")
private String privateKey;
/**
* @Description 获取会话内容
* @Param []
* @Return void
* @Author linxi
* @Date 2025/3/13 10:04
**/
public void getHuiHuaNeiRong() {
// TODO 获取newSwq
//使用sdk前需要初始化,初始化时请填入自己企业的corpid与secrectkey。
long sdkIndex = Finance.NewSdk();
long ret = Finance.Init(sdkIndex, qiYeId, hhcdSecret);
if (ret != 0) {
Finance.DestroySdk(sdkIndex);
log.error("QxBaseApi.getHuiHuaNeiRong-->获取会话内容时初始化SDK失败,返回结果: {}", ret);
}
//每次使用GetChatData拉取存档前需要调用NewSlice获取一个slice,在使用完slice中数据后,还需要调用FreeSlice释放。
long slice = Finance.NewSlice();
// 参数说明:sdk句柄, 首次拉取时seq传0 拉取序号建议设置为上次拉取返回结果中最大序号, 拉取的最大消息条数, 代理参数1, 代理参数2, 超时时间,单位为秒,建议超时时间设置为5s, NewSlice获取的slice,
ret = Finance.GetChatData(sdkIndex, 12, 1000, null, null, 5, slice); // 返回是否调用成功 0:成功 !=0失败
// 返回是否调用成功 0:成功 !=0失败
if (ret == 0) {
String dataStr = Finance.GetContentFromSlice(slice);
JSONObject data = JSONUtil.parseObj(dataStr);
// 0表示成功,错误返回非0错误码
if (data.getInt("errcode") == 0) {
JSONArray chatdataJo = data.getJSONArray("chatdata");
long maxSeq = 0;
for (int i = 0; i < chatdataJo.size(); i++) {
JSONObject jo = chatdataJo.getJSONObject(i);
// 处理最大seq
maxSeq = Math.max(jo.getLong("seq"), maxSeq);
String encrypt_key = decryptEncrypt(jo.getStr("encrypt_random_key"));
// 加密成功
if (!"".equals(encrypt_key)) {
//每次使用DecryptData解密会话存档前需要调用NewSlice获取一个slice,在使用完slice中数据后,还需要调用FreeSlice释放。
long msg = Finance.NewSlice();
// 参数说明:sdk句柄, 拉取会话存档返回的encrypt_random_key,使用配置在企业微信管理台的rsa公钥对应的私钥解密后得到encrypt_key, 拉取会话存档返回的encrypt_chat_msg
ret = Finance.DecryptData(sdkIndex, encrypt_key, jo.getStr("encrypt_chat_msg"), msg);
// 解密成功
if (ret == 0) {
String infoDataStr = Finance.GetContentFromSlice(msg);
JSONObject infoJo = JSONUtil.parseObj(infoDataStr);
// 判断是文本消息
if ("text".equals(infoJo.getStr("msgtype"))) {
// TODO 解析数据用于下一步存储
log.info("QxBaseApi.getHuiHuaNeiRong-->解密后消息: {}", infoDataStr);
}
} else {
log.warn("QxBaseApi.getHuiHuaNeiRong-->解密消息密文失败状态码: {}", ret);
}
// 销毁slice
Finance.FreeSlice(msg);
} else {
log.warn("QxBaseApi.getHuiHuaNeiRong-->encrypt_random_key解密到encrypt_key失败: {}", data.getInt("errcode"));
}
}
// TODO 存储newSwq和解析的数据
} else {
log.warn("QxBaseApi.getHuiHuaNeiRong-->获取到会话数据失败: {}", data.getInt("errcode"));
}
} else {
log.error("QxBaseApi.getHuiHuaNeiRong-->获取到会话数据失败,返回结果: {}", ret);
}
// 销毁slice
Finance.FreeSlice(slice);
// 销毁sdk
Finance.DestroySdk(sdkIndex);
}
/**
* @Description encrypt_random_key解密到encrypt_key
* @Param [encryptedData] encrypt_random_key
* @Return java.lang.String encrypt_key
* @Author linxi
* @Date 2025/3/13 15:40
**/
private String decryptEncrypt(String encryptedData){
String decryptedData = null;
try {
// 将私钥字符串转换为PrivateKey对象
byte[] keyBytes = Base64.getDecoder().decode(privateKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
// 解密数据
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
decryptedData = new String(decryptedBytes);
} catch (Exception e) {
log.error("QxBaseApi.decrypt-->encrypt_random_key解密到encrypt_key失败",e);
}
return decryptedData;
}
pom.xml
<!-- 相关依赖 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.26</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
限制说明
群成员人数不可超过管理端配置的“群成员人数上限”,且最大不可超过2000人(含应用)。 每企业创建群数不可超过1000/天。
