摘要:在微信企业号开发中,首先需要调用access_token的接口,具体请看官网文档,地址为:https://work.weixin.qq.com/api/doc#10013
第一步:创建企业应用
登录企业管理后台进入“企业应用”页面,通讯录管理是企业微信默认集成的应用,可以直接开启,如果企业需要开发自定义的应用,可点击“添加应用”完成应用的添加和配置,详细步骤请参见应用概述。
第二步:开启接收消息模式
开启接收消息模式并不是必须步骤,但是如果在你的企业应用中需要用到如下功能时需提前开启接收消息模式
- 获取企业成员的地理位置信息
- 动态调整企业应用的信息
- 获取企业成员点击事件类型的应用菜单行为
- 获取企业成员通过应用给企业后台发送的消息
关于如何开启接收消息模式,请阅读接收消息模式设置章节。
第三步:获取access_token
请求方式:GET(HTTPS)
请求URL:https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=id&corpsecret=secrect
参数说明:
参数 | 必须 | 说明 |
---|---|---|
corpid | 是 | 企业ID |
corpsecret | 是 | 应用的凭证密钥 |
权限说明:
每个应用有独立的secret,所以每个应用的access_token应该分开来获取
返回结果:
{
"errcode":0,
"errmsg":"",
"access_token":"accesstoken000001",
"expires_in":7200
}
参数 | 说明 |
---|---|
access_token | 获取到的凭证,最长为512字节 |
expires_in | 凭证的有效时间(秒) |
出错返回示例:
{
"errcode":40091,
"errmsg":"provider_secret is invalid"
}
上面都是微信企业号官网文档里面的描述,下面附上我写的代码:
1.HttpClient调用工具类:JHttpUtils.java
package com.eqiao.bidata.common.util;import com.google.common.collect.Maps;
import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** 网页处理工具** @author cc**/
public class JHttpUtils {private static Logger logger = LoggerFactory.getLogger(JHttpUtils.class);private static PoolingHttpClientConnectionManager connMgr;private static RequestConfig requestConfig;private static final int MAX_TIMEOUT = 7000;static {// 设置连接池connMgr = new PoolingHttpClientConnectionManager();// 设置连接池大小connMgr.setMaxTotal(100);connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());RequestConfig.Builder configBuilder = RequestConfig.custom();// 设置连接超时configBuilder.setConnectTimeout(MAX_TIMEOUT);// 设置读取超时configBuilder.setSocketTimeout(MAX_TIMEOUT);// 设置从连接池获取连接实例的超时configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);requestConfig = configBuilder.build();}/*** 发送 GET 请求(HTTP),不带输入数据** @param url* @return*/public static String doGet(String url) {return doGet(url, "UTF-8", Maps.newHashMap());}/*** 发送 GET 请求(HTTP),K-V形式** @param url* @param params* @return*/public static String doGet(String url, String charset,Map<Object, Object> params){StringBuffer param = new StringBuffer();int i = 0;for (Object key : params.keySet()) {if (i == 0)param.append("?");elseparam.append("&");param.append(key).append("=").append(params.get(key));i++;}url += param;String result = null;HttpClient httpClient = HttpClients.createSystem();/*HttpClient httpClient = HttpsClient.newHttpsClient();*/try {HttpGet httpGet = new HttpGet(url);HttpResponse response = httpClient.execute(httpGet);HttpEntity entity = response.getEntity();if (entity != null) {InputStream instream = entity.getContent();result = IOUtils.toString(instream, charset);}} catch (IOException e) {e.printStackTrace();logger.error("http get error: " + e.getMessage().toString());}return result;}/*** 发送 POST 请求(HTTP),不带输入数据** @param url* @return*/public static String doPost(String url) {return doPost(url, "UTF-8", Maps.newHashMap());}/*** 发送 POST 请求(HTTP),K-V形式** @param url* API接口URL* @param params* 参数map* @return*/public static String doPost(String url, String charset,Map<Object, Object> params) {CloseableHttpClient httpClient = HttpClients.createDefault();String httpStr = null;HttpPost httpPost = new HttpPost(url);CloseableHttpResponse response = null;try {httpPost.setConfig(requestConfig);List<NameValuePair> pairList = new ArrayList<NameValuePair>(params.size());for (Map.Entry<Object, Object> entry : params.entrySet()) {NameValuePair pair = new BasicNameValuePair(entry.getKey().toString(), entry.getValue().toString());pairList.add(pair);}httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName(charset)));response = httpClient.execute(httpPost);HttpEntity entity = response.getEntity();httpStr = EntityUtils.toString(entity, charset);} catch (IOException e) {e.printStackTrace();} finally {if (response != null) {try {EntityUtils.consume(response.getEntity());} catch (IOException e) {e.printStackTrace();}}}return httpStr;}
}
2.加载database.properties文件的工具类:PropertyUtil.java
package com.eqiao.bidata.common.util;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.*;
import java.util.Properties;/*** @author zhaoxinguo on 2017/7/14.*/
public class PropertyUtil {private static final Logger logger = LoggerFactory.getLogger(PropertyUtil.class);private static Properties props;static{loadProps();}synchronized static private void loadProps(){logger.info("开始加载properties文件内容.......");props = new Properties();InputStream in = null;try {in = PropertyUtil.class.getClassLoader().getResourceAsStream("bidata/properties/database.properties");props.load(in);} catch (FileNotFoundException e) {logger.error("jdbc.properties文件未找到");} catch (IOException e) {logger.error("出现IOException");} finally {try {if(null != in) {in.close();}} catch (IOException e) {logger.error("jdbc.properties文件流关闭出现异常");}}logger.info("加载properties文件内容完成...........");
// logger.info("properties文件内容:" + props);}public static String getProperty(String key){if(null == props) {loadProps();}return props.getProperty(key);}public static String getProperty(String key, String defaultValue) {if(null == props) {loadProps();}return props.getProperty(key, defaultValue);}}
3.database.properties文件:
#--weixin --
weixin.agentid=1000003
weixin.corpid=wx01ee8f220c82c2b3
weixin.corpsecret=uLQ3YxRHohuhKVM1EK5ahKTzucxuntKoj8i-fPmIXJU
4.存放微信接口地址的常量类:WeiXinQiYeConstants.java
package com.eqiao.bidata.common.weixin;/*** Created by zhaoxinguo on 2017/7/11.*/
public class WeiXinQiYeConstants {// 获取access_token的urlpublic static final String ACCESS_TOKER_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";// 根据code获取成员信息的urlpublic static final String GET_OAUTH2_URL = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE&agentid=AGENTID";}
5.AccessToken.java
package com.eqiao.bidata.common.weixin;/*** Created by zhaoxinguo on 2017/7/11.*/
public class AccessToken {// 错误codeprivate String errcode;// 错误msgprivate String errmsg;// 获取到的凭证private String access_token;// 凭证有效时间,单位:秒private Long expires_in;public String getErrcode() {return errcode;}public void setErrcode(String errcode) {this.errcode = errcode;}public String getErrmsg() {return errmsg;}public void setErrmsg(String errmsg) {this.errmsg = errmsg;}public String getAccess_token() {return access_token;}public void setAccess_token(String access_token) {this.access_token = access_token;}public Long getExpires_in() {return expires_in;}public void setExpires_in(Long expires_in) {this.expires_in = expires_in;}@Overridepublic String toString() {return "AccessToken{" +"errcode='" + errcode + '\'' +", errmsg='" + errmsg + '\'' +", access_token='" + access_token + '\'' +", expires_in=" + expires_in +'}';}
}
6.JsonMapper.java
package com.eqiao.bidata.common.util;import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.IOException;
import java.util.TimeZone;/*** 简单封装Jackson,实现JSON String<->Java Object的Mapper.* 封装不同的输出风格, 使用不同的builder函数创建实例.* @author cc*/
public class JsonMapper extends ObjectMapper {private static final long serialVersionUID = 1L;private static Logger logger = LoggerFactory.getLogger(JsonMapper.class);private static JsonMapper mapper;public JsonMapper() {this(Include.NON_EMPTY);}public JsonMapper(Include include) {// 设置输出时包含属性的风格if (include != null) {this.setSerializationInclusion(include);}// 允许单引号、允许不带引号的字段名称this.enableSimple();// 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);// 空值处理为空串this.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>(){@Overridepublic void serialize(Object value, JsonGenerator jgen,SerializerProvider provider) throws IOException,JsonProcessingException {jgen.writeString("");}});// 进行HTML解码。this.registerModule(new SimpleModule().addSerializer(String.class, new JsonSerializer<String>(){@Overridepublic void serialize(String value, JsonGenerator jgen,SerializerProvider provider) throws IOException,JsonProcessingException {jgen.writeString(StringEscapeUtils.unescapeHtml4(value));}}));// 设置时区this.setTimeZone(TimeZone.getDefault());//getTimeZone("GMT+8:00")}/*** 创建只输出非Null且非Empty(如List.isEmpty)的属性到Json字符串的Mapper,建议在外部接口中使用.*/public static JsonMapper getInstance() {if (mapper == null){mapper = new JsonMapper().enableSimple();}return mapper;}/*** 创建只输出初始值被改变的属性到Json字符串的Mapper, 最节约的存储方式,建议在内部接口中使用。*/public static JsonMapper nonDefaultMapper() {if (mapper == null){mapper = new JsonMapper(Include.NON_DEFAULT);}return mapper;}/*** Object可以是POJO,也可以是Collection或数组。* 如果对象为Null, 返回"null".* 如果集合为空集合, 返回"[]".*/public String toJson(Object object) {try {return this.writeValueAsString(object);} catch (IOException e) {logger.warn("write to json string error:" + object, e);return null;}}/*** 反序列化POJO或简单Collection如List<String>.* * 如果JSON字符串为Null或"null"字符串, 返回Null.* 如果JSON字符串为"[]", 返回空集合.* * 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String,JavaType)* @see #fromJson(String, JavaType)*/public <T> T fromJson(String jsonString, Class<T> clazz) {if (StringUtils.isEmpty(jsonString)) {return null;}try {return this.readValue(jsonString, clazz);} catch (IOException e) {logger.warn("parse json string error:" + jsonString, e);return null;}}/*** 反序列化复杂Collection如List<Bean>, 先使用函數createCollectionType构造类型,然后调用本函数.* @see #createCollectionType(Class, Class...)*/@SuppressWarnings("unchecked")public <T> T fromJson(String jsonString, JavaType javaType) {if (StringUtils.isEmpty(jsonString)) {return null;}try {return (T) this.readValue(jsonString, javaType);} catch (IOException e) {logger.warn("parse json string error:" + jsonString, e);return null;}}/*** 構造泛型的Collection Type如:* ArrayList<MyBean>, 则调用constructCollectionType(ArrayList.class,MyBean.class)* HashMap<String,MyBean>, 则调用(HashMap.class,String.class, MyBean.class)*/public JavaType createCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {return this.getTypeFactory().constructParametricType(collectionClass, elementClasses);}/*** 當JSON裡只含有Bean的部分屬性時,更新一個已存在Bean,只覆蓋該部分的屬性.*/@SuppressWarnings("unchecked")public <T> T update(String jsonString, T object) {try {return (T) this.readerForUpdating(object).readValue(jsonString);} catch (JsonProcessingException e) {logger.warn("update json string:" + jsonString + " to object:" + object + " error.", e);} catch (IOException e) {logger.warn("update json string:" + jsonString + " to object:" + object + " error.", e);}return null;}/*** 輸出JSONP格式數據.*/public String toJsonP(String functionName, Object object) {return toJson(new JSONPObject(functionName, object));}/*** 設定是否使用Enum的toString函數來讀寫Enum,* 為False時時使用Enum的name()函數來讀寫Enum, 默認為False.* 注意本函數一定要在Mapper創建後, 所有的讀寫動作之前調用.*/public JsonMapper enableEnumUseToString() {this.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);this.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);return this;}/*** 支持使用Jaxb的Annotation,使得POJO上的annotation不用与Jackson耦合。* 默认会先查找jaxb的annotation,如果找不到再找jackson的。*/public JsonMapper enableJaxbAnnotation() {JaxbAnnotationModule module = new JaxbAnnotationModule();this.registerModule(module);return this;}/*** 允许单引号* 允许不带引号的字段名称*/public JsonMapper enableSimple() {this.configure(Feature.ALLOW_SINGLE_QUOTES, true);this.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);return this;}/*** 取出Mapper做进一步的设置或使用其他序列化API.*/public ObjectMapper getMapper() {return this;}/*** 对象转换为JSON字符串* @param object* @return*/public static String toJsonString(Object object){return JsonMapper.getInstance().toJson(object);}/*** JSON字符串转换为对象* @param jsonString* @param clazz* @return*/public static Object fromJsonString(String jsonString, Class<?> clazz){return JsonMapper.getInstance().fromJson(jsonString, clazz);}}
7.调用获取access_token的接口工具类:WeiXinQiYeUtil.java
package com.eqiao.bidata.common.weixin;import com.eqiao.bidata.common.util.JHttpUtils;
import com.eqiao.bidata.common.util.JsonMapper;
import com.eqiao.bidata.common.util.PropertyUtil;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;import java.util.HashMap;
import java.util.Map;/*** 微信企业号调用类 {"errcode":0,"errmsg":"ok"} 此结果表示调用方法成功返回* Created by zhaoxinguo on 2017/7/11.*/
public class WeiXinQiYeUtil{private static Logger logger = LoggerFactory.getLogger(WeiXinQiYeUtil.class);/*** 获取access_token** @return access_token*/public static AccessToken access_token(){AccessToken accessToken = null;// https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=id&corpsecret=secrectMap<Object, Object> params = new HashMap<>();params.put("corpid", PropertyUtil.getProperty("weixin.corpid"));params.put("corpsecret",PropertyUtil.getProperty("weixin.corpsecret"));String access_token = JHttpUtils.doGet(WeiXinQiYeConstants.ACCESS_TOKER_URL, "UTF-8", params);logger.info("return access_token: " + access_token);accessToken = (AccessToken)JsonMapper.fromJsonString(access_token,AccessToken.class);/*logger.info("accessToken: " + accessToken);*/if(accessToken != null){return accessToken;}return null;}
}
8.验证接口的单元测试类:WeiXinQiYeTest.java
package com.eqiao.bidata.test;import com.eqiao.bidata.common.weixin.AccessToken;
import com.eqiao.bidata.common.weixin.WeiXinQiYeUtil;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** Created by zhaoxinguo on 2017/7/11.*/
public class WeiXinQiYeTest {private Logger logger = LoggerFactory.getLogger(this.getClass());@Testpublic void access_token(){AccessToken accessToken = WeiXinQiYeUtil.access_token();logger.info("accessToken " + accessToken.toString());}}
9.接口调用结果:
2017-07-2521:02:35 CST INFO com.eqiao.bidata.common.util.PropertyUtil 21 loadProps - 开始加载properties文件内容.......
2017-07-2521:02:35 CST INFO com.eqiao.bidata.common.util.PropertyUtil 40 loadProps - 加载properties文件内容完成...........
2017-07-2521:02:37 CST INFO com.eqiao.bidata.common.weixin.WeiXinQiYeUtil 34 access_token - return access_token: {"errcode":0,"errmsg":"ok","access_token":"Tur3OdBNDF5ek9mnaU3APIYV9V8dIgYem3ulStRS3NIBIdWOfeCTVymT_5eAcdINYAeu0TafJOzxt-XYSHLjn_s94tOlWb8dw2XmEp6mOUqYpGrOWnlvnW3A4Yonc-wt_o6OmPaiUHi7MxtzUL-4oCKvYTHg9P7ciqx-G4DNUlpiWL5uAQczjIBxRYRacALm2-nkWK_QtvvIuKUctjoabP6vT7iGfCiS7g7DcUc6iSIruAvNzPWHJhnC4G84v5Kf7Qk2wN-l8PtGioPCTSGHBX_Vxd-cLatl7CLbaqZ8cwE","expires_in":7200}
2017-07-2521:02:37 CST INFO com.eqiao.bidata.test.WeiXinQiYeTest 19 access_token - accessToken AccessToken{errcode='0', errmsg='ok', access_token='Tur3OdBNDF5ek9mnaU3APIYV9V8dIgYem3ulStRS3NIBIdWOfeCTVymT_5eAcdINYAeu0TafJOzxt-XYSHLjn_s94tOlWb8dw2XmEp6mOUqYpGrOWnlvnW3A4Yonc-wt_o6OmPaiUHi7MxtzUL-4oCKvYTHg9P7ciqx-G4DNUlpiWL5uAQczjIBxRYRacALm2-nkWK_QtvvIuKUctjoabP6vT7iGfCiS7g7DcUc6iSIruAvNzPWHJhnC4G84v5Kf7Qk2wN-l8PtGioPCTSGHBX_Vxd-cLatl7CLbaqZ8cwE', expires_in=7200}Process finished with exit code 0
以上就是调用access_token接口的全部过程。
10.源码下载地址:https://gitee.com/micai-code/weixin-qiye
11.如果还是有问题可以加群讨论解决: