java对接企业微信
Posted spream至尊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java对接企业微信相关的知识,希望对你有一定的参考价值。
java对接企业微信
一、注册企业微信
1.1 简介
企业微信与微信具有一样的体验,通过企业内部与外部客户的管理,构建出社群生态。企业微信提供丰富的api进行调用获取数据管理,也提供各种回调事件。
1.2 注册
登录官网,一键注册即可。链接: 企业微信
1.2 填写主要信息
企业微信中填写相关企业信息和负责人,然后创建。进入即可添加所需要的微信人员。
之后进行通讯录同步(此步骤为最重要一点),同步过后通讯录的人员根据调用接口接收消息。
1.3 创建应用
创建自己需要的应用,并根据提示创建应用(也可以不创建,用以前有的应用作为发送消息的主体也可以)
二、企业微信基础信息
创建完企业微信和自己所需要的应用后,需要记住几处重要的参数,方便后续调用api使用。
简单介绍几个参数:(需存入数据库)
1.企业id:当前企业微信的固定id;
2.agentid:当前自建应用id;
3.secret:应用秘钥,调用验证秘钥;
注:企业应用的可见范围需添加微信中的人员表。为后续调用查询、发送人员作限定选择。
三、消息接收url机制-回调
在java集成企业微信的同时,需要一个回调服务。现可以实现:
自定义丰富的服务行为、可以及时获取状态变化。
通过企业微信服务器向后端服务器发送各种需要消息。
这里只讲配置建立消息接收url机制。
附录上述两张图,其中包含设置可信ip(开发服务器的ip,为了服务器调用api)、配置接收服务器(通过回调机制+配置,验证url是否能接收到消息,并返回企业微信所需要的信息来校验)。加密解密见附录五。
回调配置
能通过发送来的密文(附录5中具体解密),返回给企业微信端需要的密文。即可通过校验,然后就可以填写认证ip进行开发调用接口。
简单介绍几个参数:(需存入数据库)
1.url:接收企业微信消息的接口
2.token:计算签名(可在配置中随机生成)
3.encoding_aes_key:加密key(加解密使用)
四、java对接企业微信(自建应用)
在上几个步骤中,我们已有需求的5个参数配置。根据这5个参数就可以进行与java对接。
4.1 对接前提(获取access_token)
token是调用接口的基础。根据之前的保存参数即可获取。以下展示为项目所需接口调用,其他接口使用token基本都能调用。
4.2 手机号获取userid
4.3 发送应用消息
本文只展示文本方面,其他发送信息方面可参照api接口。api接口
五、附加-校验加密解密方式
5.1 方案说明
5.2 代码实例
/**
* @Author sprem至尊
* @Date 2022/10/21 16:43
* @Version 1.0
*/
package com.example.demo.controller;
import com.example.demo.aes.WXBizJsonMsgCrypt;
import com.example.demo.cache.TokenCache;
import com.example.demo.entity.TestEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController
@Autowired
private TokenCache tokenCache;
@RequestMapping("/")
private String home(TestEntity testEntity) //postman的参数放在form-data中 不然特殊符号会变空格
String sToken = "axBoJ9yw3NeLPffBNUTQlfXiP612Xdpw";//SSrYt12q2KL QDG6eK
String sCorpID = "ww1ca8f5fe0f582941e9";//""wwca8f511fe0f58941e9"; wx5823bf96d3bd561c7
String sEncodingAESKey = "Sb8eDJndNDPjIyzqFzMI2sKtOfgoCYMrIWjqu2m1ql3Vb";// W5UBw9XoqW61HPfL7TUsI4qC2myJrw3MQ9bDrbDsWWQ2
//jWmYm7qr5nMoAUwZRjGtBxmz3K4A1tkAj3ykkR6q2B2C
String sVerifyMsgSig = testEntity.getMsg_signature();
String sVerifyTimeStamp = testEntity.getTimestamp();
String sVerifyNonce = testEntity.getNonce();
String sVerifyEchoStr = testEntity.getEchostr();
String sEchoStr ="";//明文
try
//获取其中 aesKey
WXBizJsonMsgCrypt wxcpt = new WXBizJsonMsgCrypt(sToken, sEncodingAESKey, sCorpID);
//获取明文
sEchoStr = wxcpt.VerifyURL(sVerifyMsgSig,sVerifyTimeStamp,sVerifyNonce,
sVerifyEchoStr);
System.out.println("verifyurl echostr: " + sEchoStr);
catch (Exception e)
e.printStackTrace();
return sEchoStr;
@RequestMapping("/testCache")
private String testCache()
String s = TokenCache.getValue("s1111");
if(s==null)
System.out.println("时间过期");
TokenCache.setKey("s12111","1243123123");
s =TokenCache.getValue("s12111");
System.out.println(s);
return "sss";
/**
* @Author sprem至尊
* @Date 2022/10/21 17:00
* @Version 1.0
*/
package com.example.demo.entity;
import lombok.Data;
@Data
public class TestEntity
private String msg_signature;
private String timestamp;
private String nonce;
private String echostr;
package com.example.demo.aes;
@SuppressWarnings("serial")
public class AesException extends Exception
public final static int OK = 0;
public final static int ValidateSignatureError = -40001;
public final static int ParseJsonError = -40002;
public final static int ComputeSignatureError = -40003;
public final static int IllegalAesKey = -40004;
public final static int ValidateCorpidError = -40005;
public final static int EncryptAESError = -40006;
public final static int DecryptAESError = -40007;
public final static int IllegalBuffer = -40008;
public final static int EncodeBase64Error = -40009;
public final static int DecodeBase64Error = -40010;
public final static int GenReturnJsonError = -40011;
private int code;
private static String getMessage(int code)
switch (code)
case ValidateSignatureError:
return "签名验证错误";
case ParseJsonError:
return "json解析失败";
case ComputeSignatureError:
return "sha加密生成签名失败";
case IllegalAesKey:
return "SymmetricKey非法";
case ValidateCorpidError:
return "corpid校验失败";
case EncryptAESError:
return "aes加密失败";
case DecryptAESError:
return "aes解密失败";
case IllegalBuffer:
return "解密后得到的buffer非法";
case EncodeBase64Error:
return "base64加密错误";
case DecodeBase64Error:
return "base64解密错误";
case GenReturnJsonError:
return "josn生成失败";
default:
return null; // cannot be
public int getCode()
return code;
AesException(int code)
super(getMessage(code));
this.code = code;
package com.example.demo.aes;
import java.util.ArrayList;
class ByteGroup
ArrayList<Byte> byteContainer = new ArrayList<Byte>();
public byte[] toBytes()
byte[] bytes = new byte[byteContainer.size()];
for (int i = 0; i < byteContainer.size(); i++)
bytes[i] = byteContainer.get(i);
return bytes;
public ByteGroup addBytes(byte[] bytes)
for (byte b : bytes)
byteContainer.add(b);
return this;
public int size()
return byteContainer.size();
/**
* 对企业微信发送给企业后台的消息加解密示例代码.
*
* @copyright Copyright (c) 1998-2020 Tencent Inc.
*/
// ------------------------------------------------------------------------
package com.example.demo.aes;
/**
* 针对 org.json.JSONObject,
* 要编译打包架包json
* 官方源码下载地址 : https://github.com/stleary/JSON-java, jar包下载地址 : https://mvnrepository.com/artifact/org.json/json
*/
import org.json.JSONObject;
/**
* JsonParse class
*
* 提供提取消息格式中的密文及生成回复消息格式的接口.
*/
class JsonParse
/**
* 提取出 JSON 包中的加密消息
* @param jsontext 待提取的json字符串
* @return 提取出的加密消息字符串
* @throws AesException
*/
public static Object[] extract(String jsontext) throws AesException
Object[] result = new Object[3];
try
JSONObject json = new JSONObject(jsontext);
String encrypt_msg = json.getString("encrypt");
String tousername = json.getString("tousername");
String agentid = json.getString("agentid");
result[0] = tousername;
result[1] = encrypt_msg;
result[2] = agentid;
return result;
catch (Exception e)
e.printStackTrace();
throw new AesException(AesException.ParseJsonError);
/**
* 生成json消息
* @param encrypt 加密后的消息密文
* @param signature 安全签名
* @param timestamp 时间戳
* @param nonce 随机字符串
* @return 生成的json字符串
*/
public static String generate(String encrypt, String signature, String timestamp, String nonce)
String format = "\\"encrypt\\":\\"%1$s\\",\\"msgsignature\\":\\"%2$s\\",\\"timestamp\\":\\"%3$s\\",\\"nonce\\":\\"%4$s\\"";
return String.format(format, encrypt, signature, timestamp, nonce);
/**
* 对企业微信发送给企业后台的消息加解密示例代码.
*
* @copyright Copyright (c) 1998-2014 Tencent Inc.
*/
// ------------------------------------------------------------------------
package com.example.demo.aes;
import java.nio.charset.Charset;
import java.util.Arrays;
/**
* 提供基于PKCS7算法的加解密接口.
*/
class PKCS7Encoder
static Charset CHARSET = Charset.forName("utf-8");
static int BLOCK_SIZE = 32;
/**
* 获得对明文进行补位填充的字节.
*
* @param count 需要进行填充补位操作的明文字节个数
* @return 补齐用的字节数组
*/
static byte[] encode(int count)
// 计算需要填充的位数
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
if (amountToPad == 0)
amountToPad = BLOCK_SIZE;
// 获得补位所用的字符
char padChr = chr(amountToPad);
String tmp = new String();
for (int index = 0; index < amountToPad; index++)
tmp += padChr;
return tmp.getBytes(CHARSET);
/**
* 删除解密后明文的补位字符
*
* @param decrypted 解密后的明文
* @return 删除补位字符后的明文
*/
static byte[] decode(byte[] decrypted)
int pad = (int) decrypted[decrypted.length - 1];
if (pad < 1 || pad > 32)
pad = 0;
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
/**
* 将数字转化成ASCII码对应的字符,用于对明文进行补码
*
* @param a 需要转化的数字
* @return 转化得到的字符
*/
static char chr(int a)
byte target = (byte) (a & 0xFF);
return (char) target;
/**
* 对企业微信发送给企业后台的消息加解密示例代码.
*
* @copyright Copyright (c) 1998-2014 Tencent Inc.
*/
// ------------------------------------------------------------------------
package com.example.demo.aes;
import java.security.MessageDigest;
import java.util.Arrays;
/**
* SHA1 class
*
* 计算消息签名接口.
*/
class SHA1
/**
* 用SHA1算法生成安全签名
* @param token 票据
* @param timestamp 时间戳
* @param nonce 随机字符串
* @param encrypt 密文
* @return 安全签名
* @throws AesException
*/
public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException
try
String[] array = new String[] token, timestamp, nonce, encrypt ;
StringBuffer sb = new StringBuffer();
// 字符串排序
Arrays.sort(array);
for (int i = 0; i < 4; i++)
sb.append(array[i]);
String str = sb.toString();
// SHA1签名生成
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(str.getBytes());
byte[] digest = md.digest();
StringBuffer hexstr = new StringBuffer();
String shaHex = "";
for (int i = 0; i < digest.length; i++)
shaHex = Integer.toHexString(digest[i] & 0xFF);
if (shaHex.length() < 2)
hexstr.append(0);
hexstr.append(shaHex);
return hexstr.toString();
catch (Exception e)
e.printStackTrace();
throw new AesException(AesException.ComputeSignatureError);
/**
* 对企业微信发送给企业后台的消息加解密示例代码.
*
* @copyright Copyright (c) 1998-2014 Tencent Inc.
*/
// ------------------------------------------------------------------------
/**
* 针对org.apache.commons.codec.binary.Base64,
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本)
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
*/
package com.example.demo.aes;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Random;
/**
* 提供接收和推送给企业微信消息的加解密接口(UTF8编码的字符串).
* <ol>
* <li>第三方回复加密消息给企业微信</li>
* <li>第三方收到企业微信发送的消息,验证消息的安全性,并对消息进行解密。</li>
* </ol>
* 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案
* <ol>
* <li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:
* http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
* <li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>
* <li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\\lib\\security目录下覆盖原来的文件</li>
* <li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\\jre\\lib\\security目录下覆盖原来文件</li>
* </ol>
*/
public class WXBizJsonMsgCrypt
static Charset CHARSET = Charset.forName("utf-8");
Base64 base64 = new Base64();
byte[] aesKey;
String token;
String receiveid;
/**
* 构造函数
* @param token 企业微信后台,开发者设置的token
* @param encodingAesKey 企业微信后台,开发者设置的EncodingAESKey
企业微信系列之JSSDK使用权限签名对接
企业微信系列之JSSDK使用权限签名对接
最近在对接企业微信,要将H5页面嵌在APP里,所以得根据企业微信官网规范,先对接JS-SDK使用权限签名
官网:JS-SDK使用权限签名算法
引用官方文档的说法:
签名生成规则如下:
参与签名的参数有四个: noncestr(随机字符串), jsapi_ticket(如何获取参考“获取企业jsapi_ticket”以及“获取应用的jsapi_ticket接口”), timestamp(时间戳), url(当前网页的URL, 不包含#及其后面部分)
将这些参数使用URL键值对的格式 (即 key1=value1&key2=value2…)拼接成字符串string1。
有两个注意点:1. 字段值采用原始值,不要进行URL转义;2. 必须严格按照如下格式拼接,不可变动字段顺序。
jsapi_ticket=JSAPITICKET&noncestr=NONCESTR×tamp=TIMESTAMP&url=URL
然后对string1作sha1加密即可。
根据官网提示,我们其实主要获取 jsapi_ticket这个ticket,里面分为企业的jsapi_ticket和应用的jsapi_ticket
/**
* <h2>获取JS SDK签名ticket</h2>
* @Author nicky
* @Date 2021/04/25 20:23
* @return java.lang.String
*/
public String getJsapiTicket() throws Exception {
String token = obtainAccessToken();
Assert.hasText(token, "数广AccessToken获取不到");
String ticket = "";
Object cache = CacheManager.get(WX_TICKET_CACHE_KEY);
if (cache != null) {
ticket = cache.toString();
log.info("从缓存读取Ticket:{}",ticket);
if (StringUtils.isNotBlank(ticket)) {
return ticket;
}
}
// ticket获取URI
String getTicketUri = wxParam.getTickeUri();
// 拼装ticket URL
String getTicketUrl = new StringBuffer().append(wxParam.getDomain()).append(getTicketUri).toString();
log.info("getTicketUrl:{}", getTicketUrl);
// 构建请求参数
Map<String, String> params = new HashMap<String, String>(2);
params.put("access_token" , token);
// 远程调用
String result = doGet(getTicketUrl , params , NumConstant.COMMON_NUM_ONE);
JSONObject retJson = JSONObject.parseObject(result);
log.info("ticket接口返回:{}",result);
if(NumConstant.COMMON_NUM_ZERO.equals(retJson.getString("errcode"))){
log.info("errcode:{}",retJson.getString("errcode"));
log.info("ticket:{}",retJson.getString("ticket"));
ticket = retJson.getString("ticket");
CacheManager.put(WX_TICKET_CACHE_KEY , ticket, wxParam.getExpire());
}
return ticket;
}
代码参考:
/**
* 获取AccessToken <br>
* @Author nicky
* @Date 2021/04/25 20:19
* @return java.lang.String
*/
public String obtainAccessToken() throws Exception{
String accessToken = "";
Object cache = CacheManager.get(WX_TOKEN_CACHE_KEY);
if (cache != null) {
accessToken = cache.toString();
log.info("从缓存读取AccepToken:{}",accessToken);
if (StringUtils.isNotBlank(accessToken)) {
return accessToken;
}
}
// 企业ID
String corpid = wxParam.getCorpid();
// 应用的凭证密钥
String corpsecret = wxParam.getCorpsecret();
// accessToken获取URI
String getTokenUri = wxParam.getTokenUri();
// 拼装accessToken URL
String getTokenUrl = new StringBuffer().append(wxParam.getDomain()).append(getTokenUri).toString();
log.info("obtainAccessToken方法参数打印, corpid:{},corpsecret:{} ", corpid, corpsecret);
log.info("getTokenUrl:{}", getTokenUrl);
// 构建请求参数
Map<String, String> params = new HashMap<String, String>(2);
params.put("corpid" , corpid);
params.put("corpsecret" , corpsecret);
// 远程调用
String result = doGet(getTokenUrl , params , NumConstant.COMMON_NUM_ONE);
JSONObject retJson = JSONObject.parseObject(result);
log.info("token接口返回:{}",result);
if(NumConstant.COMMON_NUM_ZERO.equals(retJson.getString("errcode"))){
log.info("errcode:{}",retJson.getString("errcode"));
log.info("accessToken:{}",retJson.getString("access_token"));
accessToken = retJson.getString("access_token");
CacheManager.put(WX_TOKEN_CACHE_KEY , accessToken, wxParam.getExpire());
}
return accessToken;
}
步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式
(即key1=value1&key2=value2…)拼接成字符串string1
/**
* <h2>获取数广JS-SDK配置参数</h2>
* @Author nicky
* @Date 2021/04/28 17:29
* @return com.minstone.module.thirdParty.digitalgd.model.vo.AgentConfigParam
*/
public AgentConfigParam obtainConfigParam(String url) throws Exception {
log.info("url:{}",url);
String ticket = this.getJsapiTicket();
Assert.hasText(ticket , "Ticket获取不到");
String corpid = wxParam.getCorpid();
String nonceStr = RandomStringUtils.randomAlphanumeric(10);
String timeStamp = Long.toString(System.currentTimeMillis() / 1000);
log.info("corpid:{},nonceStr:{},timeStamp:{}", corpid , nonceStr , timeStamp);
SortedMap<String, String> params = new TreeMap<String, String>();
params.put("noncestr" , nonceStr);
params.put("jsapi_ticket" , ticket);
params.put("timestamp" , timeStamp);
params.put("url" , url);
String signature = sortSignByASCII(params);
log.info("step1:待签名参数按照字段名的ASCII码从小到大排序:{}",signature);
signature = sha1Digest(signature);
log.info("step2:对string1进行sha1签名,得到signature:{}",signature);
AgentConfigParam configParam = AgentConfigParam.builder()
.corpid(corpid)
.nonceStr(nonceStr)
.timestamp(timeStamp)
.signature(signature)
.build();
return configParam;
}
/**
* <h2>对所有待签名参数按照字段名的ASCII 码从小到大排序</h2>
* @Author nicky
* @Date 2021/04/25 20:22
* @Param [params]
* @return java.lang.String
*/
public static String sortSignByASCII(SortedMap<String , String> parameters) {
// 以k1=v1&k2=v2...方式拼接参数
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, String> s : parameters.entrySet()) {
String k = s.getKey();
String v = s.getValue();
// 过滤空值
if (StringUtils.isBlank(v)) {
continue;
}
builder.append(k).append("=").append(v).append("&");
}
if (!parameters.isEmpty()) {
builder.deleteCharAt(builder.length() - 1);
}
return builder.toString();
}
步骤2. 对string1进行sha1签名,得到signature
/**
* sha1加密 <br>
* @Author nicky
* @Date 2021/04/26 10:22
* @Param [str]
* @return java.lang.String
*/
public static String sha1Digest(String str) {
try {
// SHA1签名生成
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(str.getBytes());
byte[] digest = md.digest();
StringBuffer hexstr = new StringBuffer();
String shaHex = "";
for (int i = 0; i < digest.length; i++) {
shaHex = Integer.toHexString(digest[i] & 0xFF);
if (shaHex.length() < 2) {
hexstr.append(0);
}
hexstr.append(shaHex);
}
return hexstr.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
测试页面:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="/axios.min.js"></script>
<script src="/eruda.js"></script>
<script src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<title>Document</title>
</head>
<body>
<script>
window.onload = function () {
/*调试工具而已*/
eruda.init();
let url = location.href.split("#")[0]
axios.get('/getWxConfig?url=' + decodeURI(url)).then(response => {
let configParam = response.data; // console.log(configParam)
wx.config({
beta: true,// 调用wx.invoke形式的接口值时,该值必须为true。
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: configParam.appId, // 必填cropID
timestamp: Number.parseInt(configParam.timestamp), // 必填,生成签名的时间戳
nonceStr: configParam.nonceStr, // 必填,生成签名的随机串
signature: configParam.signature,// 必填,签名,见附录1
jsApiList: ["getNetworkType", "agentConfig","selectEnterpriseContact"] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
// 用到了三个 selectEnterpriseContact和getNetworkType和agentConfig 都配置了
});
wx.ready(function (res) {
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
console.log("=================config成功ready函数中=============")
// 不是jssdk所有的函数都需要agentConfig的,跟用户相关的操作可能需要,具体请参考文档
axios.get('/agentConfig?url=' + decodeURI(url)).then(response => {
let agentConfigParam = response.data; //console.log(agentConfigParam)
wx.invoke("agentConfig", {
agentid: agentConfigParam.agentid, // 必填,单位应用的agentid
corpid: agentConfigParam.corpid, // 必填的corpid
timestamp: Number.parseInt(agentConfigParam.timestamp), // 必填,生成签名的时间戳,int类型, 如 1539100800
nonceStr: agentConfigParam.nonceStr, // 必填,生成签名的随机串
signature: agentConfigParam.signature,// 必填,签名,见附录5
}, function (res) {
alert(JSON.stringify(res))
console.log(res)
if (res.err_msg != "agentConfig:ok" && res.errMsg != "agentConfig:ok") {
return;
}
// 下面是另外一个组件具体用法参考文档
wx.invoke("selectEnterpriseContact", {
"fromDepartmentId": -1,// 必填,-1表示打开的通讯录从自己所在部门开始展示, 0表示从最上层开始
"mode": "multi",// 必填,选择模式,single表示单选,multi表示多选
"type": ["user"],// 必填,选择限制类型,指定department、user中的一个或者多个
},function(res){
alert(JSON.stringify(res))
})
});
}).catch(function (error) { // 请求失败处理
console.log(error);
});
wx.getNetworkType({
success: function (res) {
console.log(res)
}
});
})
wx.error(function (res) {
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
console.log("====================config失败error函数中===============================")
});
}).catch(function (error) { // 请求失败处理
console.log(error);
});
}
</script>
</body>
</html>
以上是关于java对接企业微信的主要内容,如果未能解决你的问题,请参考以下文章