微信公众平台安全模式下传输xml数据包时解密方式

大家好,我是雄雄,欢迎你的到来。

前言:

最近一直在搞微信服务(公众)号开发,前面也写过一篇文章,是关于一开始配置、验证token以及接收用户触发关注/取消关注事件时的接口,文章地址:java实现微信服务(公众)号用户关注时,获取openid,安全模式下的加密解密实现

今天将加密解密的工具类汇总整理一下发出来,记录一下。

加密/解密工具类

/*** 对公众平台发送给公众账号的消息加解密示例代码.* * @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 org.jeecg.modules.utils;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 appId;/*** 构造函数* @param token 公众平台上,开发者设置的token* @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey* @param appId 公众平台appid* * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息*/public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws AesException {if (encodingAesKey.length() != 43) {throw new AesException(AesException.IllegalAesKey);}this.token = token;this.appId = appId;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[] appidBytes = appId.getBytes(CHARSET);// randomStr + networkBytesOrder + text + appidbyteCollector.addBytes(randomStrBytes);byteCollector.addBytes(networkBytesOrder);byteCollector.addBytes(textBytes);byteCollector.addBytes(appidBytes);// ... + 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_appid;try {// 去除补位字符byte[] bytes = PKCS7Encoder.decode(original);// 分离16位随机字符串,网络字节序和AppIdbyte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);int xmlLength = recoverNetworkBytesOrder(networkOrder);xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);from_appid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length),CHARSET);} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.IllegalBuffer);}// appid不相同的情况if (!from_appid.equals(appId)) {throw new AesException(AesException.ValidateAppidError);}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;}}

官方提供的测试类:

package org.jeecg.modules.utils;import static org.junit.Assert.*;import java.io.IOException;
import java.io.StringReader;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;import org.junit.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;public class WXBizMsgCryptTest {String encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";String tokeN = "";String timestamp = "1409304348";String nonce = "xxxxxx";String appId = "wxb11529c136998cb6";String replyMsg = "我是中文abcd123";String xmlFormat = "<xml><><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>";String afterAesEncrypt = "jn1L23DB+6ELqJ++3bnyg5K55bgVtVzd832WzZGMhkP72vVOfg==";String randomStr = "aaaabbbbccccdddd";String replyMsg2 = "<xml><ToUserName><![CDATA[oia2Tj我是中文jewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>";String afterAesEncrypt2 = "jn1L23DB+6ELqJ+6bruv23M2GmYfkv0xBh2h+XTBOKVKcgDFHle6gqcZ1cZrk3e1qjPQ1F4RsLWzQRG9udbKWesxlkupqcEcW7ZQweImX9+wLMa0GaUzpkycA8+IamDBxn5loLgZpnS7fVAbExOkK5DYHBmv5tptA9tklE/fTIILHR8HLXa5nQvFb3tYPKAlHF3rtTeayNf0QuM+UW/wM9enGIDIJHF7CLHiDNAYxr+r+OrJCmPQyTy8cVWlu9iSvOHPT/77bZqJucQHQ04sq7KZI27OcqpQNSto2OdHCoTccjggX5Z9Mma0nMJBU+jLKJ38YB1fBIz+vBzsYjrTmFQ44YfeEuZ+xRTQwr92vhA9OxchWVINGC50qE/6lmkwWTwGX9wtQpsJKhP+oS7rvTY8+VdzETdfakjkwQ5/Xka042OlUb1/slTwo4RscuQ+RdxSGvDahxAJ6+EAjLt9d8igHngxIbf6YyqqROxuxqIeIch3CssH/LqRs+iAcILvApYZckqmA7FNERspKA5f8GoJ9sv8xmGvZ9Yrf57cExWtnX8aCMMaBropU/1k+hKP5LVdzbWCG0hGwx/dQudYR/eXp3P0XxjlFiy+9DMlaFExWUZQDajPkdPrEeOwofJb";@BeforeClasspublic static void setUpBeforeClass() throws Exception {}@AfterClasspublic static void tearDownAfterClass() throws Exception {}@Beforepublic void setUp() throws Exception {}@Afterpublic void tearDown() throws Exception {}@Testpublic void testNormal() throws ParserConfigurationException, SAXException, IOException {try {WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);String afterEncrpt = pc.encryptMsg(replyMsg, timestamp, nonce);DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();DocumentBuilder db = dbf.newDocumentBuilder();StringReader sr = new StringReader(afterEncrpt);InputSource is = new InputSource(sr);Document document = db.parse(is);Element root = document.getDocumentElement();NodeList nodelist1 = root.getElementsByTagName("Encrypt");NodeList nodelist2 = root.getElementsByTagName("MsgSignature");String encrypt = nodelist1.item(0).getTextContent();String msgSignature = nodelist2.item(0).getTextContent();String fromXML = String.format(xmlFormat, encrypt);// 第三方收到公众号平台发送的消息String afterDecrpt = pc.decryptMsg(msgSignature, timestamp, nonce, fromXML);assertEquals(replyMsg, afterDecrpt);} catch (AesException e) {fail("正常流程,怎么就抛出异常了??????");}}@Testpublic void testAesEncrypt() {try {WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);assertEquals(afterAesEncrypt, pc.encrypt(randomStr, replyMsg));} catch (AesException e) {e.printStackTrace();fail("no异常");}}@Testpublic void testAesEncrypt2() {try {WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);assertEquals(afterAesEncrypt2, pc.encrypt(randomStr, replyMsg2));} catch (AesException e) {e.printStackTrace();fail("no异常");}}@Testpublic void testIllegalAesKey() {try {new WXBizMsgCrypt(token, "abcde", appId);} catch (AesException e) {assertEquals(AesException.IllegalAesKey, e.getCode());return;}fail("错误流程不抛出异常???");}@Testpublic void testValidateSignatureError() throws ParserConfigurationException, SAXException,IOException {try {WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);String afterEncrpt = pc.encryptMsg(replyMsg, timestamp, nonce);DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();DocumentBuilder db = dbf.newDocumentBuilder();StringReader sr = new StringReader(afterEncrpt);InputSource is = new InputSource(sr);Document document = db.parse(is);Element root = document.getDocumentElement();NodeList nodelist1 = root.getElementsByTagName("Encrypt");String encrypt = nodelist1.item(0).getTextContent();String fromXML = String.format(xmlFormat, encrypt);pc.decryptMsg("12345", timestamp, nonce, fromXML); // 这里签名错误} catch (AesException e) {assertEquals(AesException.ValidateSignatureError, e.getCode());return;}fail("错误流程不抛出异常???");}@Testpublic void testVerifyUrl() throws AesException {WXBizMsgCrypt wxcpt = new WXBizMsgCrypt("QDG6eK","jWmYm7qr5nMoAUwZRjGtBxmz3KA1tkAj3ykkR6q2B2C", "wx5823bf96d3bd56c7");String verifyMsgSig = "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3";String timeStamp = "1409659589";String nonce = "263014780";String echoStr = "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ==";wxcpt.verifyUrl(verifyMsgSig, timeStamp, nonce, echoStr);// 只要不抛出异常就好}
}

我的解密测试

这个是我写的解密测试类,其实也差不多,只是我为了后面维护方便,将共用的信息提取到了常量类中:

package org.jeecg.modules.utils;import org.jeecg.modules.config.WeChatContant;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;/*** @author: muxiongxiong* @date: 2022年08月17日 下午 9:21* 公众号:雄雄的小课堂* 博客:https://blog.csdn.net/qq_34137397* 个人站:http://www.穆雄雄.com* 个人站:http://www.muxiongxiong.cn* @Description: 官方提供的加解密demo类*/
public class Program {// 需要加密的明文String encodingAesKey = WeChatContant.ENCODINGAESKEY;String token = WeChatContant.TOKEN;String timestamp = "1409304348";String nonce = "xxxxxx";String appId = WeChatContant.APPID;/*** main方法* @param args* @throws Exception*/public static void main(String[] args) throws Exception {Program program = new Program();program.decode();}/*** 解密测试*/public  void decode() throws AesException {//构造函数WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);String miwen = "5Fhy9chrfcshHmeCrAKLBZmJdYWm1RwDNYOyCfbVC6L1MUAUto5xOimdotgLBSCKRyt+yys+o+5aO0nRdO4XfS9CAhYK50NeO6O/M8woX0fanUn/DjYGJV4Mm/Zfg7KmpkXXxwguo5RJrYweglVOHZ96tLWZppAA5/LDl26YF2TmaBWAwPZU1n8R1Wg8jCaSolXBOYZ90lE3854n/+GV27xtpNy1+a+lPs5YPmyauL6wCCU2g2H71g4iYyz4ifi1afxmZsI6vkmKv0sncQMfkOYgX7g0pOZaQ/rnwjmnGfFpmY3DJe7ufmSyd+LA/5r93PIW0vW7j/Bf7xbw77ElrSghvA/Ro6U+xEZHNF3ZUqb5ce1P+62qPakC0r23RlJfMhTVF1yqKL0W/fM1pNbCfJC7KBX9IHja9bxPuN7QiWs=";String jiemi = pc.decrypt(miwen);System.out.println("解密后的内容是:"+jiemi);}/*** 官方提供的示例代码* @throws Exception*/public  void authorityDemo() throws Exception{String replyMsg = " 中文<xml><ToUserName><![CDATA[oia2TjjewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>";//构造函数WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);String mingwen = pc.encryptMsg(replyMsg, timestamp, nonce);System.out.println("加密后: " + mingwen);DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);dbf.setXIncludeAware(false);dbf.setExpandEntityReferences(false);DocumentBuilder db = dbf.newDocumentBuilder();StringReader sr = new StringReader(mingwen);InputSource is = new InputSource(sr);Document document = db.parse(is);Element root = document.getDocumentElement();NodeList nodelist1 = root.getElementsByTagName("Encrypt");NodeList nodelist2 = root.getElementsByTagName("MsgSignature");String encrypt = nodelist1.item(0).getTextContent();String msgSignature = nodelist2.item(0).getTextContent();String format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>";String fromXML = String.format(format, encrypt);//// 公众平台发送消息给第三方,第三方处理//// 第三方收到公众号平台发送的消息String result2 = pc.decryptMsg(msgSignature, timestamp, nonce, fromXML);System.out.println("解密后明文: " + result2);//pc.verifyUrl(null, null, null, null);}}

然后就可以了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nssl1148,jzoj5461-购物【可撤回贪心,堆】

正题 jzoj题目链接:https://jzoj.net/senior/#main/show/5461 题目大意 有n个物品&#xff0c;m元&#xff0c;k个打折券。 每个物品打折前pip_ipi​元&#xff0c;打折后qiq_iqi​元&#xff0c;求最多能买多少物品。 解题思路 用可撤回贪心。 先将p和q分开排序&#xff0c…

“.Net 社区大会”(dotnetConf) 2017 Day 1 Keynote: .NET Everywhere

8月份已经发布了.NET Core 2.0, 大会Keynote 一开始花了大量的篇幅回顾.NET Core 2.0的发布&#xff0c;社区的参与度已经非常高。大会的主题是.NET 无处不在&#xff1a;NET Core 2.0已经完成了服务端的布局&#xff0c;那么各种终端的覆盖就是Xamarin的主场&#xff0c;Xamar…

金三银四铜五铁六

转载自 金三银四铜五铁六 金三银四铜五铁六 据说&#xff0c;金三银四&#xff0c;截止今天为止面试黄金时间已经过去十之八九&#xff0c;而鲁班&#xff08;LB &#xff0c;以下全文均用LB代替&#xff09;恰逢是这批面试大军其中的一名小兵&#xff0c;很不幸今年恰逢遇…

WebSocket In ASP.NET Core

What Is WebSocket?WebSocket 是一种在单个 TCP 连接上进行全双工通讯的协议&#xff0c;是建立在TCP上、且独立的协议。在WebSocket API 中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以进行持久性的连接&#xff0c;并进行双向数据传输。为了建立…

微信服务(公众)号实现用户关注自动注册成为会员

大家好&#xff0c;我是雄雄。交流技术可以进入到我的社区【雄雄的小课堂】 前言 看本篇文章之前&#xff0c;可以先看看前面两篇&#xff1a; java实现微信服务&#xff08;公众&#xff09;号用户关注时&#xff0c;获取openid&#xff0c;安全模式下的加密解密实现 微信公…

nssl1149,jzoj5455-拆网线【贪心】

正题 jzoj题目链接:https://jzoj.net/senior/#main/show/5455 题目大意 一棵树中选k个点要求留下最小边使每个点都至少有一个点连接。 解题思路 我们先不考虑树的限制&#xff0c;那么k个点满足要求的话最小边数为(k1)/2(k1)/2(k1)/2&#xff0c;然后我们考虑一颗树上有多少…

完成图片拖拽

<html> <head><meta charset"UTF-8"><title>拖拽</title><style type"text/css">#box1{width: 100px;height: 100px;background-color: yellow;position:absolute;}#box2{width: 100px;height: 100px;background-colo…

深入探索 Java 热部署

转载自 深入探索 Java 热部署 简介 在 Java 开发领域&#xff0c;热部署一直是一个难以解决的问题&#xff0c;目前的 Java 虚拟机只能实现方法体的修改热部署&#xff0c;对于整个类的结构修改&#xff0c;仍然需要重启虚拟机&#xff0c;对类重新加载才能完成更新操作。对…

手把手教你使用spring cloud+dotnet core搭建微服务架构:服务治理(-)

背景公司去年开始使用dotnet core开发项目。公司的总体架构采用的是微服务&#xff0c;那时候由于对微服务的理解并不是太深&#xff0c;加上各种组件的不成熟&#xff0c;只是把项目的各个功能通过业务层面拆分&#xff0c;然后通过nginx代理&#xff0c;项目最终上线。但是这…

谈谈那些被面与面的经历(面试杂谈)

大家好&#xff0c;我是雄雄。 前言 每个程序员&#xff0c;都会经历大大小小的面试&#xff0c;当你在一个公司做技术大牛时&#xff0c;你可能充当的是一个面试官的角色&#xff0c;此时由你来面别人&#xff1b;当你要去一个公司做大牛时&#xff0c;此时的你&#xff0c;充…

nssl1150,jzoj5309-密室【分层建图,SPFA】

正题 jzoj题目链接:https://jzoj.net/senior/#main/show/5309 题目大意 有n个点&#xff0c;m条边,k种钥匙。有些点分布了钥匙&#xff0c;有些边需要一些钥匙才可以通过&#xff0c;求1到n的最短路。 解题思路 将图分成2k2k层&#xff0c;每一层用二进制表示不同的钥匙情况…

如何在面试中介绍自己的项目经验

转载自 如何在面试中介绍自己的项目经验 在面试时&#xff0c;经过寒暄后&#xff0c;一般面试官会让介绍项目经验 。常见的问法是&#xff0c;说下你最近的&#xff08;或最拿得出手的&#xff09;一个项目。 根据我们的面试经验&#xff0c;发现有不少候选人对此没准备&am…

Configuration Extensions - 简化配置,让你配置支持变量

在开发“RabbitCloud”项目时&#xff0c;使用配置文件发现会有很多重复值&#xff0c;所以我基于”Microsoft.Extensions.Configuration”写了一个扩展库&#xff0c;来丰富对配置的支持。实际案例——“RabbitCloud”之前&#xff0c;我是这样的因为公司ip和家里机器的ip不一…

如何配置frp到linux服务器和windows本地,服务端支持自启动

大家好&#xff0c;我是雄雄&#xff0c;如果你觉得文章还不错的话&#xff0c;欢迎在文末点赞和评论。 前言 最近这段时间都在开发微信服务&#xff08;公众&#xff09;号相关技术&#xff0c;对于写了好几年的后端程序的我来说&#xff0c;开发小程序和服务号&#xff0c;刚…

插入链接

简单的插入链接&#xff0c;直接提供跳转页面项 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><a href"oneclick.html">点我一下&#xff0c;有惊喜</a>…

ssl提高组周六模拟赛【2018.9.15】

前言 这次那了一个和fuW并列的第一&#xff0c;然而似乎都不是很难。(因为第1题有bug所以满分不了) 成绩 只放Rank 1∼10Rank1∼10RankRankPersonPersonScoreScore112015hjw" role="presentation" style="position: relative;">2015hjw2015hjw19…

java面试线程必备知识点,怼死面试官,从我做起

转载自 java面试线程必备知识点&#xff0c;怼死面试官&#xff0c;从我做起 |--多线程一定好么&#xff1f; cpu密集不好 io密集好 |--如何减少上下文切换&#xff1a; 无锁并发&#xff08;数据id根据Hash分段&#xff09;、CAS、最少线程 |--java线程避免死锁&#xff…

错误代码:88000, 错误信息:without comment privilege hint: [7oJ0533w689] rid: 630432cd-15944cf6-083e04fc

大家好&#xff0c;我是雄雄。 问题复现 这个问题&#xff0c;是我在操作&#xff1a;给微信公众平台上添加图文到草稿箱时&#xff0c;遇到的。 报错信息如下&#xff1a; {"errcode":88000,"errmsg":"without comment privilege hint: [7oJ0533w6…

asp.net core AuthenticationMiddleware 在WebApi中的的使用

在.net framework 4.5架构下使用认证&#xff08;Authentication&#xff09;授权(Authorization)。IIS使用HttpModule进行认证&#xff08;Authentication&#xff09;&#xff0c;我们可以选择自己实现认证方式并在web.config中配置&#xff0c;当然也可以选择IIS默认提供的几…

初衷以及目的

因为我学的是软件工程专业的嘛&#xff0c;当初高考误打误撞选择了这门专业&#xff0c;也算是满意吧&#xff0c;毕竟自己对计算机还是挺感兴趣的。 很早以前我就想把计算机学好&#xff0c;希望用华丽的编程技术去创造有趣的游戏&#xff0c;但一直因为各种因素直到高三我才…