企業微信開發之回調

   註冊企業微信,開發企業微信服務商管理後臺,應用管理-網頁應用,建立一個應用。具體如下:

圖一
圖二
圖三

 

       數據回調和指令回調,可以是一個接口,也可以是分開,在這裏我使用的是一個接口。

       數據回調是直接調用,查看是否接口可以調通。指令回調是給後端的接口傳送推一些數據,比如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 是一個實體類,裏面就是幾個字段。

    至此,已經完成了回調,如有不對之處,請指正。

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章