企业微信开启接收消息+验证URL有效性
企业微信开启接收消息+验证URL有效性
📔 千寻简笔记介绍
千寻简笔记已开源,Gitee与GitHub搜索chihiro-notes
,包含笔记源文件.md
,以及PDF版本方便阅读,且是用了精美主题,阅读体验更佳,如果文章对你有帮助请帮我点一个Star
~
更新:支持在线阅读文章,根据发布日期分类。
文章目录
- 企业微信开启接收消息+验证URL有效性
- 📔 千寻简笔记介绍
- 简介
- 本文关键词
- 实现步骤
- 1 开启接收消息
- 1.1 设置接收消息的参数
- 2 验证URL有效性
- 2.1 官方说明
- 2.2 Java代码
- 控制器层代码:`QYWeiXinLoginController.java`
- 业务接口:`IQYWeiXinLoginService.java`
- 业务接口实现:`QYWeiXinLoginServiceImpl.java`
- 2.3 使用到的示例代码
- 提供接收和推送给企业微信消息的加解密接口:`WXBizMsgCrypt.java`
- 加解密异常类:`AesException.java`
- 提供基于PKCS7算法的加解密接口:`PKCS7Encoder.java`
- 计算消息签名接口:`SHA1.java`
- 提供提取消息格式中的密文及生成回复消息格式的接口:`XMLParse.java`
简介
前期准备
- 一个备案的域名。
- 可线上部署的应用,与域名对应。
2.3使用到的示例代码来源企业微信-开发者中心
-
访问地址:
https://developer.work.weixin.qq.com/document/path/90468
-
示例代码下载链接:
https://open.work.weixin.qq.com/wwopen/downloadfile/java.zip
关于企业微信的开启接收信息的文档
https://developer.work.weixin.qq.com/document/10514
本文关键词
企业微信开启接收消息
、验证URL有效性
、SHA1
、提供接收和推送给企业微信消息的加解密接口
、计算消息签名接口
实现步骤
1 开启接收消息
1.1 设置接收消息的参数
在企业的管理端后台,进入需要设置接收消息的目标应用,点击“接收消息”的“设置API接收”按钮,进入配置页面。
用的URL、Token、EncodingAESKey三个参数
- URL是企业后台接收企业微信推送请求的访问协议和地址,支持http或https协议。
- Token可由企业任意填写,用于生成签名。
- EncodingAESKey用于消息体的加密,是AES密钥的Base64编码。
这三个参数需要在下面代码中使用到。
2 验证URL有效性
2.1 官方说明
当点击“保存”提交以上信息时,企业微信会发送一条验证消息到填写的URL,发送方法为GET。
企业的接收消息服务器接收到验证请求后,需要作出正确的响应才能通过URL验证。
假设接收消息地址设置为:http://api.3dept.com/
,企业微信将向该地址发送如下验证请求:
请求方式:GET
请求地址:
http://api.3dept.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFSS×tamp=13500001234&nonce=123412323&echostr=ENCRYPT_STR
参数说明
参数 | 必须 | 说明 |
---|---|---|
msg_signature | 是 | 企业微信加密签名,msg_signature结合了企业填写的token、请求中的timestamp、nonce参数、加密的消息体 |
timestamp | 是 | 时间戳 |
nonce | 是 | 随机数 |
echostr | 是 | 加密的字符串。需要解密得到消息内容明文,解密后有random、msg_len、msg、CorpID四个字段,其中msg即为消息内容明文 |
企业后台收到请求后,需要做如下操作:
- 对收到的请求做Urldecode处理
- 通过参数msg_signature对请求进行校验,确认调用者的合法性。
- 解密echostr参数得到消息内容(即msg字段)
- 在1秒内原样返回明文消息内容(不能加引号,不能带bom头,不能带换行符)
2.2 Java代码
控制器层代码:QYWeiXinLoginController.java
package com.ruoyi.project.controller;import com.ruoyi.project.tx.qyweixin.service.IQYWeiXinLoginService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@RestController
@RequestMapping("/wechat/info")
public class QYWechatInfoController {@Resourceprivate IQYWeiXinLoginService qyWeiXinLoginService;/*** 验证URL有效性**/@RequestMapping(value = "/verifyURLValidity", method = RequestMethod.GET)public void verifyURLValidity(@RequestParam(value = "msg_signature", required = true) String msgSignature,@RequestParam(value = "timestamp", required = true) String timestamp,@RequestParam(value = "nonce", required = true) String nonce,@RequestParam(value = "echostr", required = true) String echostr,HttpServletResponse response) {String msg = qyWeiXinLoginService.verifyURLValidity(msgSignature, timestamp, nonce, echostr);try {response.getWriter().print(msg);} catch (IOException e) {throw new RuntimeException(e);}}}
业务接口:IQYWeiXinLoginService.java
package com.ruoyi.project.tx.qyweixin.service;public interface IQYWeiXinLoginService {String verifyURLValidity(String msgSignature, String timestamp, String nonce, String echostr);
}
业务接口实现:QYWeiXinLoginServiceImpl.java
package com.ruoyi.project.tx.qyweixin.service.impl;import com.ruoyi.project.tx.qyweixin.service.IQYWeiXinLoginService;
import com.ruoyi.project.utils.aes.AesException;
import com.ruoyi.project.utils.aes.WXBizMsgCrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;@Service
public class QYWeiXinLoginServiceImpl implements IQYWeiXinLoginService {private static final Logger logger = LoggerFactory.getLogger(QYWeiXinLoginServiceImpl.class);@Overridepublic String verifyURLValidity(String msgSignature, String timestamp, String nonce, String echostr) {logger.info("企业微信登录获取到的参数:msgSignature:{},timestamp:{},nonce:{},echostr:{}:",msgSignature,timestamp,nonce,echostr);//tokenString TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";// encodingAESKeyString ENCODINGAES_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";//企业IDString CORP_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";// 通过检验msg_signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败String result = null;try {WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(TOKEN, ENCODINGAES_KEY, CORP_ID);result = wxcpt.VerifyURL(msgSignature, timestamp, nonce, echostr);} catch (AesException e) {e.printStackTrace();}logger.info("----------微信接口访问结束----------");return result;}
}
2.3 使用到的示例代码
提供接收和推送给企业微信消息的加解密接口:WXBizMsgCrypt.java
/*** 对企业微信发送给企业后台的消息加解密示例代码.** @copyright Copyright (c) 1998-2014 Tencent Inc.*/// ------------------------------------------------------------------------/*** 针对org.apache.commons.codec.binary.Base64,* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本)* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi*/
package com.ruoyi.project.utils.aes;import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Random;import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;import org.apache.commons.codec.binary.Base64;/*** 提供接收和推送给企业微信消息的加解密接口(UTF8编码的字符串).* <ol>* <li>第三方回复加密消息给企业微信</li>* <li>第三方收到企业微信发送的消息,验证消息的安全性,并对消息进行解密。</li>* </ol>* 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案* <ol>* <li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:* http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>* <li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>* <li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li>* <li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li>* </ol>*/
public class WXBizMsgCrypt {static Charset CHARSET = Charset.forName("utf-8");Base64 base64 = new Base64();byte[] aesKey;String token;String receiveid;/*** 构造函数* @param token 企业微信后台,开发者设置的token* @param encodingAesKey 企业微信后台,开发者设置的EncodingAESKey* @param receiveid, 不同场景含义不同,详见文档** @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 + "=");}// 生成4个字节的网络字节序byte[] getNetworkBytesOrder(int sourceNumber) {byte[] orderBytes = new byte[4];orderBytes[3] = (byte) (sourceNumber & 0xFF);orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);return orderBytes;}// 还原4个字节的网络字节序int recoverNetworkBytesOrder(byte[] orderBytes) {int sourceNumber = 0;for (int i = 0; i < 4; i++) {sourceNumber <<= 8;sourceNumber |= orderBytes[i] & 0xff;}return sourceNumber;}// 随机生成16位字符串String getRandomStr() {String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < 16; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}/*** 对明文进行加密.** @param text 需要加密的明文* @return 加密后base64编码的字符串* @throws AesException aes加密失败*/String encrypt(String randomStr, String text) throws AesException {ByteGroup byteCollector = new ByteGroup();byte[] randomStrBytes = randomStr.getBytes(CHARSET);byte[] textBytes = text.getBytes(CHARSET);byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length);byte[] receiveidBytes = receiveid.getBytes(CHARSET);// randomStr + networkBytesOrder + text + receiveidbyteCollector.addBytes(randomStrBytes);byteCollector.addBytes(networkBytesOrder);byteCollector.addBytes(textBytes);byteCollector.addBytes(receiveidBytes);// ... + pad: 使用自定义的填充方式对明文进行补位填充byte[] padBytes = PKCS7Encoder.encode(byteCollector.size());byteCollector.addBytes(padBytes);// 获得最终的字节流, 未加密byte[] unencrypted = byteCollector.toBytes();try {// 设置加密模式为AES的CBC模式Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);// 加密byte[] encrypted = cipher.doFinal(unencrypted);// 使用BASE64对加密后的字符串进行编码String base64Encrypted = base64.encodeToString(encrypted);return base64Encrypted;} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.EncryptAESError);}}/*** 对密文进行解密.** @param text 需要解密的密文* @return 解密得到的明文* @throws AesException aes解密失败*/String decrypt(String text) throws AesException {byte[] original;try {// 设置解密模式为AES的CBC模式Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES");IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);// 使用BASE64对密文进行解码byte[] encrypted = Base64.decodeBase64(text);// 解密original = cipher.doFinal(encrypted);} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.DecryptAESError);}String xmlContent, from_receiveid;try {// 去除补位字符byte[] bytes = PKCS7Encoder.decode(original);// 分离16位随机字符串,网络字节序和receiveidbyte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);int xmlLength = recoverNetworkBytesOrder(networkOrder);xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);from_receiveid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length),CHARSET);} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.IllegalBuffer);}// receiveid不相同的情况if (!from_receiveid.equals(receiveid)) {throw new AesException(AesException.ValidateCorpidError);}return xmlContent;}/*** 将企业微信回复用户的消息加密打包.* <ol>* <li>对要发送的消息进行AES-CBC加密</li>* <li>生成安全签名</li>* <li>将消息密文和安全签名打包成xml格式</li>* </ol>** @param replyMsg 企业微信待回复用户的消息,xml格式的字符串* @param timeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp* @param nonce 随机串,可以自己生成,也可以用URL参数的nonce** @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息*/public String EncryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException {// 加密String encrypt = encrypt(getRandomStr(), replyMsg);// 生成安全签名if (timeStamp == "") {timeStamp = Long.toString(System.currentTimeMillis());}String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt);// System.out.println("发送给平台的签名是: " + signature[1].toString());// 生成发送的xmlString result = XMLParse.generate(encrypt, signature, timeStamp, nonce);return result;}/*** 检验消息的真实性,并且获取解密后的明文.* <ol>* <li>利用收到的密文生成安全签名,进行签名验证</li>* <li>若验证通过,则提取xml中的加密消息</li>* <li>对消息进行解密</li>* </ol>** @param msgSignature 签名串,对应URL参数的msg_signature* @param timeStamp 时间戳,对应URL参数的timestamp* @param nonce 随机串,对应URL参数的nonce* @param postData 密文,对应POST请求的数据** @return 解密后的原文* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息*/public String DecryptMsg(String msgSignature, String timeStamp, String nonce, String postData)throws AesException {// 密钥,公众账号的app secret// 提取密文Object[] encrypt = XMLParse.extract(postData);// 验证安全签名String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());// 和URL中的签名比较是否相等// System.out.println("第三方收到URL中的签名:" + msg_sign);// System.out.println("第三方校验签名:" + signature);if (!signature.equals(msgSignature)) {throw new AesException(AesException.ValidateSignatureError);}// 解密String result = decrypt(encrypt[1].toString());return result;}/*** 验证URL* @param msgSignature 签名串,对应URL参数的msg_signature* @param timeStamp 时间戳,对应URL参数的timestamp* @param nonce 随机串,对应URL参数的nonce* @param echoStr 随机串,对应URL参数的echostr** @return 解密之后的echostr* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息*/public String VerifyURL(String msgSignature, String timeStamp, String nonce, String echoStr)throws AesException {String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr);if (!signature.equals(msgSignature)) {throw new AesException(AesException.ValidateSignatureError);}String result = decrypt(echoStr);return result;}}
加解密异常类:AesException.java
package com.ruoyi.project.utils.aes;/*** 加解密异常类*/
public class AesException extends Exception {public final static int OK = 0;public final static int ValidateSignatureError = -40001;public final static int ParseXmlError = -40002;public final static int ComputeSignatureError = -40003;public final static int IllegalAesKey = -40004;public final static int ValidateCorpidError = -40005;public final static int EncryptAESError = -40006;public final static int DecryptAESError = -40007;public final static int IllegalBuffer = -40008;
//public final static int EncodeBase64Error = -40009;
//public final static int DecodeBase64Error = -40010;
//public final static int GenReturnXmlError = -40011;private int code;private static String getMessage(int code) {switch (code) {case ValidateSignatureError:return "签名验证错误";case ParseXmlError:return "xml解析失败";case ComputeSignatureError:return "sha加密生成签名失败";case IllegalAesKey:return "SymmetricKey非法";case ValidateCorpidError:return "corpid校验失败";case EncryptAESError:return "aes加密失败";case DecryptAESError:return "aes解密失败";case IllegalBuffer:return "解密后得到的buffer非法";
// case EncodeBase64Error:
// return "base64加密错误";
// case DecodeBase64Error:
// return "base64解密错误";
// case GenReturnXmlError:
// return "xml生成失败";default:return null; // cannot be}}public int getCode() {return code;}AesException(int code) {super(getMessage(code));this.code = code;}
}
ByteGroup.java
package com.ruoyi.project.utils.aes;import java.util.ArrayList;class ByteGroup {ArrayList<Byte> byteContainer = new ArrayList<Byte>();public byte[] toBytes() {byte[] bytes = new byte[byteContainer.size()];for (int i = 0; i < byteContainer.size(); i++) {bytes[i] = byteContainer.get(i);}return bytes;}public ByteGroup addBytes(byte[] bytes) {for (byte b : bytes) {byteContainer.add(b);}return this;}public int size() {return byteContainer.size();}
}
提供基于PKCS7算法的加解密接口:PKCS7Encoder.java
package com.ruoyi.project.utils.aes;import java.nio.charset.Charset;
import java.util.Arrays;/*** 提供基于PKCS7算法的加解密接口.*/
class PKCS7Encoder {static Charset CHARSET = Charset.forName("utf-8");static int BLOCK_SIZE = 32;/*** 获得对明文进行补位填充的字节.** @param count 需要进行填充补位操作的明文字节个数* @return 补齐用的字节数组*/static byte[] encode(int count) {// 计算需要填充的位数int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);if (amountToPad == 0) {amountToPad = BLOCK_SIZE;}// 获得补位所用的字符char padChr = chr(amountToPad);String tmp = new String();for (int index = 0; index < amountToPad; index++) {tmp += padChr;}return tmp.getBytes(CHARSET);}/*** 删除解密后明文的补位字符** @param decrypted 解密后的明文* @return 删除补位字符后的明文*/static byte[] decode(byte[] decrypted) {int pad = (int) decrypted[decrypted.length - 1];if (pad < 1 || pad > 32) {pad = 0;}return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);}/*** 将数字转化成ASCII码对应的字符,用于对明文进行补码** @param a 需要转化的数字* @return 转化得到的字符*/static char chr(int a) {byte target = (byte) (a & 0xFF);return (char) target;}}
计算消息签名接口:SHA1.java
package com.ruoyi.project.utils.aes;import java.security.MessageDigest;
import java.util.Arrays;
/*** 对企业微信发送给企业后台的消息加解密示例代码.** @copyright Copyright (c) 1998-2014 Tencent Inc.*/
/*** SHA1 class** 计算消息签名接口.*/
public class SHA1 {/*** 用SHA1算法生成安全签名* @param token 票据* @param timestamp 时间戳* @param nonce 随机字符串* @param encrypt 密文* @return 安全签名* @throws AesException*/public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException{try {String[] array = new String[] { token, timestamp, nonce, encrypt };StringBuffer sb = new StringBuffer();// 字符串排序Arrays.sort(array);for (int i = 0; i < 4; i++) {sb.append(array[i]);}String str = sb.toString();// SHA1签名生成MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(str.getBytes());byte[] digest = md.digest();StringBuffer hexstr = new StringBuffer();String shaHex = "";for (int i = 0; i < digest.length; i++) {shaHex = Integer.toHexString(digest[i] & 0xFF);if (shaHex.length() < 2) {hexstr.append(0);}hexstr.append(shaHex);}return hexstr.toString();} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.ComputeSignatureError);}}
}
提供提取消息格式中的密文及生成回复消息格式的接口:XMLParse.java
/*** 对企业微信发送给企业后台的消息加解密示例代码.** @copyright Copyright (c) 1998-2014 Tencent Inc.*/// ------------------------------------------------------------------------package com.ruoyi.project.utils.aes;import java.io.StringReader;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;/*** XMLParse class** 提供提取消息格式中的密文及生成回复消息格式的接口.*/
class XMLParse {/*** 提取出xml数据包中的加密消息* @param xmltext 待提取的xml字符串* @return 提取出的加密消息字符串* @throws AesException*/public static Object[] extract(String xmltext) throws AesException {Object[] result = new Object[3];try {DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();String FEATURE = null;// This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented// Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-declFEATURE = "http://apache.org/xml/features/disallow-doctype-decl";dbf.setFeature(FEATURE, true);// If you can't completely disable DTDs, then at least do the following:// Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities// Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities// JDK7+ - http://xml.org/sax/features/external-general-entitiesFEATURE = "http://xml.org/sax/features/external-general-entities";dbf.setFeature(FEATURE, false);// Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities// Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities// JDK7+ - http://xml.org/sax/features/external-parameter-entitiesFEATURE = "http://xml.org/sax/features/external-parameter-entities";dbf.setFeature(FEATURE, false);// Disable external DTDs as wellFEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";dbf.setFeature(FEATURE, false);// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"dbf.setXIncludeAware(false);dbf.setExpandEntityReferences(false);// And, per Timothy Morgan: "If for some reason support for inline DOCTYPEs are a requirement, then// ensure the entity settings are disabled (as shown above) and beware that SSRF attacks// (http://cwe.mitre.org/data/definitions/918.html) and denial// of service attacks (such as billion laughs or decompression bombs via "jar:") are a risk."// remaining parser logicDocumentBuilder db = dbf.newDocumentBuilder();StringReader sr = new StringReader(xmltext);InputSource is = new InputSource(sr);Document document = db.parse(is);Element root = document.getDocumentElement();NodeList nodelist1 = root.getElementsByTagName("Encrypt");result[0] = 0;result[1] = nodelist1.item(0).getTextContent();return result;} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.ParseXmlError);}}/*** 生成xml消息* @param encrypt 加密后的消息密文* @param signature 安全签名* @param timestamp 时间戳* @param nonce 随机字符串* @return 生成的xml字符串*/public static String generate(String encrypt, String signature, String timestamp, String nonce) {String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n"+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n"+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" + "</xml>";return String.format(format, encrypt, signature, timestamp, nonce);}
}
相关文章:

企业微信开启接收消息+验证URL有效性
企业微信开启接收消息验证URL有效性 📔 千寻简笔记介绍 千寻简笔记已开源,Gitee与GitHub搜索chihiro-notes,包含笔记源文件.md,以及PDF版本方便阅读,且是用了精美主题,阅读体验更佳,如果文章对…...

电脑访问不到在同网络的手机设备
手机连接了同网络的wifi,但是电脑ping不通手机的ip,这可能是路由出了问题,因为最终是走的mac地址,访问不了是因为电脑不知道手机的mac地址,则可以这样设置绑定mac地址,管理员权限启动cmd,然后执…...

国内MES系统应用研究报告:“企业MES应用现状”| 百世慧®
随着制造企业数字化转型需求的增强,工业软件的需求也不断被激发。 2022年,中国MES软件及服务市场规模实现23.8%的较高速增长。同时,随着工业互联网、MOM的兴起和不断发展,也推动着MES持续发展和不断迭代,如今MES向着更…...

C++模板元模板实战书籍讲解第一章题目讲解
目录 第一题 C代码示例 第二题 C代码示例 第三题 3.1 使用std::integral_constant模板类 3.2 使用std::conditional结合std::is_same判断 总结 第四题 C代码示例 第五题 C代码示例 第六题 C代码示例 第七题 C代码示例 总结 第一题 对于元函数来说,…...

Java在互联网网络安全中的应用(三)
目录 1. 互联网网络安全概述 2. Java的网络安全特性 3. 用Java加固网络应用 4. 安全传输 5. 安全框架和工具 6. 实际应用案例 7. 最佳实践和资源 目标 本次技术分享的目标是介绍Java技术在互联网网络安全中的应用,包括关键概念、最佳实践和实际案例。 1. 互…...

VMLogin如何解决跨境电商多账号管理难题?
做跨境电商的,比如亚马逊、eBay这些卖家。随着团队规模的扩大,或者多店铺运营,那么多个店铺多个账号管理就成为了一个困难。如何解决这个问题呢? 首先来看看很多电商卖家多账号管理会面临的问题 经营者通常需要同时管理多个市场…...

STM32创建工程步骤
以创建led工程为例: 新建一个led文件夹 新建一个以led命名的工程(用keil_uVision5)并添加三个组。 Library文件夹里放置库函数文件。 User: 点亮led灯的程序; 直接给寄存器赋值 调用库函数。 #include "stm…...

软考 系统架构设计师系列知识点之边缘计算(1)
所属章节: 第11章. 未来信息综合技术 第4节. 边缘计算概述 1. 边缘计算概念 在介绍边缘计算之前,有必要先介绍一下章鱼。章鱼就是用“边缘计算”来解决实际问题的。作为无脊椎动物中智商最高的一种动物,章鱼拥有巨量的神经元,但…...

vue:写一个数组box和list数组,在保留box数组中原有对象的同时,将list数组中每一个对象插入到box数组后面
前言:由于源码涉及到后端调用数据和一些无关的功能所以我就专门针对这个功能的代码,这样好方便理解。 1、在data中定义两个数组:box和list,并给它们初始化值 data() {return {box: [/*初始的box数组对象*/],list: [/*初始的list…...

Python教程:随机函数,开始猜英文单词的游戏
开始猜英文单词的游戏… 总计生命次数:3次 -----------游戏开始中…----------- ????请猜一个,4位数的单词:mafr 猜错了,再努力一下 -----------你还有2次生命------------ ma?&…...

Unit2_1:动态规划DP
文章目录 一、介绍二、0-1背包问题问题描述分析伪代码时间复杂度 三、钢条切割问题问题描述分析伪代码过程 四、矩阵链乘法背景性质分析案例伪代码 一、介绍 动态规划类似于分治法,它们都将一个问题划分为更小的子问题 最优子结构:问题的最优解包含子问题的最优解。DP适用的原…...

k8s提交spark应用消费kafka数据写入elasticsearch7
一、k8s集群环境 k8s 1.23版本,三个节点,容器运行时使用docker。 spark版本时3.3.3 k8s部署单节点的zookeeper、kafka、elasticsearch7 二、spark源码 https://download.csdn.net/download/TT1024167802/88509398 命令行提交方式 /opt/module/spark…...

linux傻瓜式安装Java环境及中间件
linux配置Java环境及中间件 1.傻瓜式安装Java1.下载2.追加3.刷新测试 2.傻瓜式安装docker1.docker卸载2.docker安装 3.Docker傻瓜式安装Redis1.傻瓜式安装安装并配置 4.Docker傻瓜式安装RabbitMQ5.Docker傻瓜式安装MySql1.拉取2.配置 6.傻瓜式安装Nacos1.官网下载nacos2.SQL文件…...

javascript中的new原理及实现
在js中,我们通过new运算符来创建一个对象,它是一个高频的操作。我们一般只是去用它,而很少关注它是如何实现的,它的工作机制是什么。 1 简介 本文介绍new的功能,用法,补充介绍了不加new也同样创建对象的方…...

R语言 PPT 预习+复习
什么狗吧发明的结业考,站出来和我对线 第一章 绪论 吊码没有,就算考R语言特点我也不背,问就是叫么这没用。 第二章 R语言入门 x<-1:20 赋值语句 x 1到20在x上添加均值为0、标准差为2的正态分布噪声 y <- x rnorm (20, 0, 2) 这…...

轻松实现固定资产智能管理的工具来了
易点易动资产管理系统是一款旨在轻松实现智能资产管理的工具。固定资产管理对于企业的日常经营和可持续发展至关重要。然而,固定资产具有设备价值高、使用周期长、使用地点分散、使用环境恶劣、流动性强、安全管理难度大等特点,传统的管理方式往往无法高…...

软考高级系统架构设计师系列之:微服务
软考高级系统架构设计师系列之:微服务 一、微服务二、微服务的优势三、微服务挑战四、微服务与SOA的对比一、微服务 微服务架构建议将大型复杂的单体架构应用划分为一组微小的服务,每个微服务根据其负责的具体业务职责提炼为单一的业务能力。每个服务可以很容易地部署并发布…...

vue + axios + mock
参考来源:Vue mock.js模拟数据实现首页导航与左侧菜单功能_vue.js_AB教程网 记录步骤:在参考资料来源添加axios步骤 1、安装mock依赖 npm install mock -D //只在开发环境使用 下载完成后,项目文件package.json中的devDependencies就会加…...

Mongoose 开源库--json 使用笔记
一、 json相关API mongoose 开源库可以使用json进行数据处理。 ①创建json字符串 // A helper macro for printing JSON: mg_snprintf(buf, len, "%m", MG_ESC("hi")) #define MG_ESC(str) mg_print_esc, 0, (str) char *mg_mprintf(const char *fmt, ...)…...

linux中复制文件如何排除一个目录
误区: 首先使用cp命令的 --exclude参数实不可取的,会造成以下的报错,因为cp命令中压根就没有--exclude这个参数的配置 cp: unrecognized option --exclude‘****’ 问题解决: 我们可以使用rsync工具来完成目录排除的功能&#x…...

时空智友企业信息管理系统任意文件读取漏洞复现
简介 时空智友企业信息管理系统是一个用于企业流程管理和控制的软件系统。它旨在帮助企业实现流程的规范化、自动化和优化,从而提高工作效率、降低成本并提升管理水平。 时空智友企业信息管理系统存在任意文件读取漏洞,攻击者可以在未授权的情况下读取…...

YOLOv8优化:block系列篇 | Neck系列篇 |可重参化EfficientRepBiPAN优化Neck
🚀🚀🚀本文改进: 可重参化EfficientRepBiPAN优化Neck 如何在YOLOv8下使用:1)结合neck; 🚀🚀🚀EfficientRepBiPAN在各个领域都有ying 🚀🚀🚀YOLOv8改进专栏:http://t.csdnimg.cn/hGhVK 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研; 1.原理…...

零代码编程:用ChatGPT批量提取flash动画swf文件中的mp3
文件夹:C:\迅雷下载\有声绘本_flash[淘宝-珍奥下载]\有声绘本 flash,里面有多个flash文件,怎么转换成mp3文件呢? 可以使用swfextract工具从Flash动画中提取音频,下载地址是http://www.swftools.org/download.html,也…...

2023数学建模国赛C题赛后总结
今天国赛的成绩终于出来了,盼星星盼月亮的。之前面试的时候已经把我给推到国奖评委那里去了,可是好可惜,最终以很微小的劣势错失国二。只拿到了广西区的省一。我心里还是很遗憾的,我真的为此准备了很久,虽然当中也有着…...

hiveSQL语法及练习题整理(mysql)
目录 hiveSQL练习题整理: 第一题 第二题 第三题 第四题 第五题 第六题 第七题 第八题 第九题 第十题 第十一题 第十二题 hivesql常用函数: hiveSQL常用操作语句(mysql) hiveSQL练习题整理: 第一题 我…...

【UE4】UE编辑器乱码问题
环境:UE4.27、vs2019 如何解决 问题原因,UE的编码默认是UTF-8,VS的默认编码是GBK 通过"高级保存选项" 直接修改VS的 .h头文件 的 编码 为 UTF-8 步骤1. 步骤2. 修改编码后,从新编译,然后就可以解决编辑器…...

2 创建svelte项目(应用程序)
官网方式搭建: npm create sveltelatest my-app cd my-app npm install npm run dev 官网中介绍: 如果您使用的是 VS Code,安装 Svelte for VS Code 就可以了,以便语法高亮显示。 然后,一旦您的项目设置好了&#…...

手机怎么打包?三个方法随心选!
有的时候,电脑不在身边,只有随身携带的手机,这个时候又急需把文件打包发送给同事或者同学,如何利用手机操作呢?下面介绍了具体的操作步骤。 一、通过手机文件管理自带压缩功能打包 1、如果是iOS系统,就在手…...

SecureFX如何用Public key 連接sftp
點擊connection 右鍵點開站點的properties 點選SSH2--Authentication---Pulickey 先選擇Putty Key Generator save出來的public key(.pub)文件(Putty Key Generator 保存時可能沒加.pub後綴保存,可自行對public key加上後綴.pub) 同時注意要…...

BUUCTF 隐藏的钥匙 1
BUUCTF:https://buuoj.cn/challenges 题目描述: 路飞一行人千辛万苦来到了伟大航道的终点,找到了传说中的One piece,但是需要钥匙才能打开One Piece大门,钥匙就隐藏在下面的图片中,聪明的你能帮路飞拿到钥匙ÿ…...