企业微信开发之回调

   注册企业微信,开发企业微信服务商管理后台,应用管理-网页应用,建立一个应用。具体如下:

图一
图二
图三

 

       数据回调和指令回调,可以是一个接口,也可以是分开,在这里我使用的是一个接口。

       数据回调是直接调用,查看是否接口可以调通。指令回调是给后端的接口传送推一些数据,比如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 是一个实体类,里面就是几个字段。

    至此,已经完成了回调,如有不对之处,请指正。

 

 

 

 

 

 

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
企业微信开发回调验证是指通过验证企业微信接收到的外部事件回调(例如消息、联系人等),确保这些事件确实是由企业微信发送的。下面给出一个用Java代码实现企业微信开发回调验证的示例: ```java import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; public class CallbackVerifier { private static final String TOKEN = "your_token"; // 企业微信后台配置的Token // 验证回调请求的签名是否合法 public static boolean verifySignature(String signature, String timestamp, String nonce) { String[] arr = {TOKEN, timestamp, nonce}; Arrays.sort(arr); // 字典序排序 StringBuilder content = new StringBuilder(); for (String s : arr) { content.append(s); } return signature.equals(sha1(content.toString())); } // 使用SHA1算法计算字符串的哈希值 private static String sha1(String str) { try { MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.update(str.getBytes()); byte[] bytes = digest.digest(); StringBuilder sb = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(b & 0xff); if (hex.length() == 1) { sb.append("0"); } sb.append(hex); } return sb.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } // 测试 public static void main(String[] args) { String signature = "signature"; // 从企业微信接收到的请求参数 String timestamp = "timestamp"; String nonce = "nonce"; boolean isValid = verifySignature(signature, timestamp, nonce); System.out.println("是否合法:" + isValid); } } ``` 在上述代码中,首先定义了一个TOKEN常量,即企业微信后台配置的Token。然后定义了一个verifySignature方法,该方法接收从企业微信接收到的signature、timestamp和nonce参数,并通过字典序排序和SHA1算法生成待验证的签名。最后,通过判断生成的签名是否与接收到的signature一致来验证回调请求的合法性。 该示例代码可以直接运行,并且可以将TOKEN替换为实际的Token进行验证。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值