註冊企業微信,開發企業微信服務商管理後臺,應用管理-網頁應用,建立一個應用。具體如下:
數據回調和指令回調,可以是一個接口,也可以是分開,在這裏我使用的是一個接口。
數據回調是直接調用,查看是否接口可以調通。指令回調是給後端的接口傳送推一些數據,比如suit_token等。
整體邏輯:
1、從request中拿到簽名(signature)、時間戳(timestamp)、隨機字符串(nonce) 和驗證回調的URL的有效性傳入的字符串(echostr).
/**url中的簽名**/
String signature = request.getParameter("msg_signature");
/**url中的時間戳*/
String timestamp = request.getParameter("timestamp");
/** url中的隨機字符串 **/
String nonce = request.getParameter("nonce");
/** 創建套件時驗證回調url有效性時傳入**/
String echostr = request.getParameter("echostr");
2、首先是一個不做任何操作,僅僅驗證該url是否可以調用的操作。需要對echostr進行解密操作,然後再將解密後的值返回給企業微信,這樣就證明這個URL可以對密文進行解密。是可以調通的。注意:這個時間在實例化WXBizMsgCrypt用的是corpId。 WXBizMsgCrypt是企業微信提供的工具類。
/**
* 企業回調的url-----該url不做任何的業務邏輯,僅僅微信查看是否可以調通
*/
WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(Token, EncodingAESKey, CorpID);
sEchoStr = wxcpt.verifyURL(signature, timestamp, nonce, echostr);
//必須要返回解密之後的明文
response.getWriter().write(sEchoStr);
WXBizMsgCrypt方法:
/**
* 構造函數
* @param token 公衆平臺上,開發者設置的token
* @param encodingAesKey 公衆平臺上,開發者設置的EncodingAESKey
* @param receiveId企業的corpid/suiteID,
*
* @throws AesException 執行失敗,請查看該異常的錯誤碼和具體的錯誤信息
*/
public WXBizMsgCrypt(String token, String encodingAesKey, String receiveId) throws AesException {
if (encodingAesKey.length() != 43) {
throw new AesException(AesException.IllegalAesKey);
}
this.token = token;
this.receiveId= receiveId;
aesKey = Base64.decodeBase64(encodingAesKey + "=");
}
3、指令回調需要傳遞一些數據到後端接口,這時候需要將這些數據(request.getInputStream)轉化爲string,然後再將這個string進行解密,轉化成xml文件,然後根據xml文件中的類型,進行操作。注意:這個在實例化的時候用的是suiteID。
首先是將流轉換爲string的方法:
然後是將流轉換爲string,然後方法得到的string解密,然後再解析xml,根據xml中的 InfoType得到不同類型的回調數據
/**
* 數據回調URL,用於接收託管企業微信應用的用戶消息和用戶事件 系統將會把此應用的授權變更事件以及ticket參數等推送給此URL
* */
String postData = getStringInputstream(request);
QywxCallBackInfo callBackInfo = QywxUtil.getDecryptCallBackInfo(postData, signature, timestamp, nonce, QywxBase.sSuiteID);
String infoType = callBackInfo.getInfoType();
logger.warn("infoType"+infoType);
if (!StringUtils.isBlank(infoType)) {
switch (infoType) {
/**
* 開始推送---票據
*/
case "suite_ticket":
if (!StringUtils.isEmpty(callBackInfo.getSuiteTicket())) {
//得到suite_ticket
logger.warn("suiteId"+callBackInfo.getSuiteId()+"ticket"+ callBackInfo.getSuiteTicket());
}
break;
case "create_auth":
/**
* 開始授權----從企業微信應用市場發起授權時,企業微信後臺會推送授權成功通知。
* 服務商的響應必須在1000ms內完成,以保證用戶安裝應用的體驗。建議在接收到此事件時,先記錄下AuthCode,並立即回 應企業微信,之後再做相關業務的處理。
*/
break;
case "change_auth":
break;
case "cancel_auth":
/**
* 取消授權
*/
break;
case "contact_sync":
break;
default:
break;
}
}
//返回值,必須是success
response.getWriter().write("success");
上面的代碼用到的相關方法如下:
/**
* 根據不同的類型得到不同的回調
*/
public static QywxCallBackInfo getDecryptCallBackInfo(String encryptPostData, String msgSignature, String timestamp, String nonce, String receiveId) throws Exception {
QywxCallBackInfo callBackInfo = new QywxCallBackInfo();
/**
* 備註:在企業內部的工具類中有!from_corpid.equals(corpId)的校驗,
* 但是在第三方應用的時候,由於postDate解密得到的是安裝該應用的fromCorpID,所以不能進行比較
*/
WXBizMsgCrypt wxBizMsgCrypt = new WXBizMsgCrypt(QywxBase.sToken, QywxBase.sEncodingAESKey, receiveId);
String decryptPostData = wxBizMsgCrypt.DecryptMsg(msgSignature, timestamp, nonce, encryptPostData);
System.out.println("decryptPostData" + decryptPostData);
//開始解析xml
Element root = getXMLCDATA(decryptPostData);
System.out.println("root" + root);
Node infoTypeNode = root.getElementsByTagName("InfoType").item(0);
Node suiteIdNode = root.getElementsByTagName("SuiteId").item(0);
Node timestampNode = root.getElementsByTagName("TimeStamp").item(0);
Node authCorpIdNode = root.getElementsByTagName("AuthCorpId").item(0);
Node authCodeNode = root.getElementsByTagName("AuthCode").item(0);
Node suiteTicketNode = root.getElementsByTagName("SuiteTicket").item(0);
Node seqNode = root.getElementsByTagName("Seq").item(0);
if (infoTypeNode != null) {
callBackInfo.setInfoType(infoTypeNode.getTextContent());
}
if (suiteIdNode != null) {
callBackInfo.setSuiteId(suiteIdNode.getTextContent());
}
if (timestampNode != null) {
callBackInfo.setTimeStamp(timestampNode.getTextContent());
}
if (authCorpIdNode != null) {
callBackInfo.setAuthCorpId(authCorpIdNode.getTextContent());
}
if (authCodeNode != null) {
callBackInfo.setAuthCode(authCodeNode.getTextContent());
}
if (suiteTicketNode != null) {
callBackInfo.setSuiteTicket(suiteTicketNode.getTextContent());
}
if (seqNode != null) {
callBackInfo.setSeq(seqNode.getTextContent());
}
return callBackInfo;
}
public static Element getXMLCDATA(String requestStr) {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(requestStr);
InputSource is = new InputSource(sr);
Document document = db.parse(is);
Element root = document.getDocumentElement();
return root;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String getStringInputstream(HttpServletRequest request) {
StringBuffer strb = new StringBuffer();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
String str = null;
while (null != (str = reader.readLine())) {
strb.append(str);
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
return strb.toString();
}
QywxCallBackInfo
是一個實體類,裏面就是幾個字段。
至此,已經完成了回調,如有不對之處,請指正。