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&timestamp=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对接企业微信的主要内容,如果未能解决你的问题,请参考以下文章

企业微信系列之JSSDK文件预览对接

企业微信系列之JSSDK使用权限签名对接

企业微信系列之JSSDK使用权限签名对接

C# 企业微信消息推送对接,实现天气预报推送

企业怎么利用微信建立企业微官网?

Java企业微信开发_07_总结一下企业微信的配置