1. 程式人生 > >微信企業號開發之access_token介面呼叫示例

微信企業號開發之access_token介面呼叫示例

第一步:建立企業應用

登入企業管理後臺進入“企業應用”頁面,通訊錄管理是企業微信預設整合的應用,可以直接開啟,如果企業需要開發自定義的應用,可點選“新增應用”完成應用的新增和配置,詳細步驟請參見應用概述

第二步:開啟接收訊息模式

開啟接收訊息模式並不是必須步驟,但是如果在你的企業應用中需要用到如下功能時需提前開啟接收訊息模式

  • 獲取企業成員的地理位置資訊
  • 動態調整企業應用的資訊
  • 獲取企業成員點選事件型別的應用選單行為
  • 獲取企業成員通過應用給企業後臺傳送的訊息

關於如何開啟接收訊息模式,請閱讀接收訊息模式設定章節。

第三步:獲取access_token

請求方式:GET(HTTPS
請求URL:

https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=id&corpsecret=secrect

引數說明:

引數 必須 說明
corpid 企業ID
corpsecret 應用的憑證金鑰

許可權說明:

每個應用有獨立的secret,所以每個應用的access_token應該分開來獲取

返回結果:

  1. {
  2. "errcode":0
  3. "errmsg":""
  4. "access_token":"accesstoken000001",
  5. "expires_in":7200
  6. }

引數 說明
access_token 獲取到的憑證,最長為512位元組
expires_in 憑證的有效時間(秒)
出錯返回示例:
  1. {
  2. "errcode":40091,
  3. "errmsg":"provider_secret is invalid"
  4. }

上面都是微信企業號官網文件裡面的描述,下面附上我寫的程式碼:

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("?");
            else
                param.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的url
    public static final String ACCESS_TOKER_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";

    // 根據code獲取成員資訊的url
    public 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 {

    // 錯誤code
    private String errcode;

    // 錯誤msg
    private 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;
    }

    @Override
    public 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>(){
			@Override
			public void serialize(Object value, JsonGenerator jgen,
					SerializerProvider provider) throws IOException,
                    JsonProcessingException {
				jgen.writeString("");
			}
        });
		// 進行HTML解碼。
		this.registerModule(new SimpleModule().addSerializer(String.class, new JsonSerializer<String>(){
			@Override
			public 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=secrect
        Map<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());

    @Test
    public 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介面的全部過程。