微信公眾號訊息、加解密、模板訊息封裝
阿新 • • 發佈:2020-08-18
轉自我的個人部落格:微信公眾號訊息、加解密、模板訊息封裝
最近又在做個公眾號相關的系統,之前寫公眾號經常動不動Map,這次實在看不下去了,基本都進行了封裝。
以此文記錄,一來以後複用,二來也是希望能幫助到有需要的人。
一、加、解密工具類EncryptUtils
public class EncryptUtils { private static Logger logger = LoggerFactory.getLogger(EncryptUtils.class); public static boolean checkSignature(String token, String signature, String timestamp, String nonce) { // 拼接字串 String[] arr = new String[] { token, timestamp, nonce }; // 排序 Arrays.sort(arr); // 生成字串 StringBuffer content = new StringBuffer(); for (int i = 0; i < arr.length; i++) { content.append(arr[i]); } // SHA1加密 String tmp = EncryptUtils.SHA1(content.toString()); return tmp.equals(signature); } /** * 用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); } } /** * 引數簽名 * @param params 參與簽名的引數 * @param key 密碼 * @param type 簽名演算法 * @return * @throws Exception */ public static String getSign(Map<String,String> params, String key, String type)throws Exception{ if (params == null || params.isEmpty()){ return null; } String[] keys = new String[params.size()]; StringBuffer sb = new StringBuffer(); Set<String> strings = params.keySet(); keys = strings.toArray(keys); // 字串排序 Arrays.sort(keys); for (int i = 0; i < keys.length; i++) { sb.append(keys[i]) .append("=") .append(params.get(keys[i])) .append("&"); } String str = sb.append("key=") .append(key).toString(); MessageDigest md = MessageDigest.getInstance(type); 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().toUpperCase(); } public static String SHA1(String decript) { try { MessageDigest digest = java.security.MessageDigest .getInstance("SHA-1"); digest.update(decript.getBytes()); byte messageDigest[] = digest.digest(); // Create Hex String StringBuffer hexString = new StringBuffer(); // 位元組陣列轉換為 十六進位制 數 for (int i = 0; i < messageDigest.length; i++) { String shaHex = Integer.toHexString(messageDigest[i] & 0xFF); if (shaHex.length() < 2) { hexString.append(0); } hexString.append(shaHex); } return hexString.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return ""; } /** * * @param decript * @return */ public static String SHA(String decript) { try { MessageDigest digest = java.security.MessageDigest .getInstance("SHA"); digest.update(decript.getBytes()); byte messageDigest[] = digest.digest(); // Create Hex String StringBuffer hexString = new StringBuffer(); // 位元組陣列轉換為 十六進位制 數 for (int i = 0; i < messageDigest.length; i++) { String shaHex = Integer.toHexString(messageDigest[i] & 0xFF); if (shaHex.length() < 2) { hexString.append(0); } hexString.append(shaHex); } return hexString.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return ""; } /** * 取字串MD5值 * @param input * @return */ public static String MD5(String input) { try { // 獲得MD5摘要演算法的 MessageDigest 物件 MessageDigest mdInst = MessageDigest.getInstance("MD5"); // 使用指定的位元組更新摘要 mdInst.update(input.getBytes()); // 獲得密文 byte[] md = mdInst.digest(); // 把密文轉換成十六進位制的字串形式 StringBuffer hexString = new StringBuffer(); // 位元組陣列轉換為 十六進位制 數 for (int i = 0; i < md.length; i++) { String shaHex = Integer.toHexString(md[i] & 0xFF); if (shaHex.length() < 2) { hexString.append(0); } hexString.append(shaHex); } return hexString.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return ""; } /** * AES加密 * * @param content * 需要加密的內容 * @param password * 加密密碼 * @return */ public static byte[] encryptAES(String content, String password) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128, new SecureRandom(password.getBytes())); SecretKey secretKey = kgen.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); Cipher cipher = Cipher.getInstance("AES");// 建立密碼器 byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化 byte[] result = cipher.doFinal(byteContent); return result; // 加密 } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } /** * AES解密 * * @param content * 待解密內容 * @param password * 解密金鑰 * @return */ public static byte[] decryptAES(byte[] content, String password) { try { KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128, new SecureRandom(password.getBytes())); SecretKey secretKey = kgen.generateKey(); byte[] enCodeFormat = secretKey.getEncoded(); SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); Cipher cipher = Cipher.getInstance("AES");// 建立密碼器 cipher.init(Cipher.DECRYPT_MODE, key);// 初始化 byte[] result = cipher.doFinal(content); return result; // 加密 } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return null; } /** * 建立微信支付金鑰 * @param params * @param key * @return */ public static String createPaySign(Map params, String key){ List ls = new ArrayList(); Set keys = params.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()){ String paramKey = iterator.next().toString(); ls.add(paramKey); } Collections.sort(ls); String str = ""; for (int i = 0; i < ls.size(); i++) { str += ls.get(i) + "=" + params.get(ls.get(i)) + "&"; } str += "key=" + key; return MD5(str).toUpperCase(); } }
二、訊息封裝
1、來自微信伺服器的訊息ReceiveMessage
@Data @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "xml") public class ReceiveMessage { @XmlElement(name = "ToUserName", required = true) private String toUserName; @XmlElement(name = "FromUserName", required = true) private String fromUserName; @XmlElement(name = "CreateTime", required = true) private Long createTime; @XmlElement(name = "MsgType", required = true) private String msgType; //文字訊息內容 @XmlElement(name = "Content") private String content; @XmlElement(name = "MsgId", required = true) private Integer msgId; @XmlElement(name = "PicUrl") private String picUrl; @XmlElement(name = "MediaId") private String mediaId; @XmlElement(name = "Format") private String format; @XmlElement(name = "Recognition") private String recognition; @XmlElement(name = "ThumbMediaId") private String thumbMediaId; //地理位置緯度 @XmlElement(name = "Location_X") private Double locationX; //地理位置經度 @XmlElement(name = "Location_Y") private Double locationY; //地圖縮放大小 @XmlElement(name = "Scale") private Integer scale; //地理位置資訊 @XmlElement(name = "Label") private String label; @XmlElement(name = "Title") private String title; @XmlElement(name = "Description") private String description; @XmlElement(name = "Url") private String url; @XmlElement(name = "Event") private String event; @XmlElement(name = "EventKey") private String eventKey; @XmlElement(name = "Ticket") private String ticket; //事件訊息 地理位置緯度 @XmlElement(name = "Latitude") private Double latitude; //事件訊息 地理位置經度 @XmlElement(name = "Longitude") private Double longitude; //地理位置精度 @XmlElement(name = "Precision") private Double precision; }
2、回覆微信伺服器的訊息ReplyMessage
@Data @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "xml") public class ReplyMessage { @JSONField(name = "touser") @XmlElement(name = "ToUserName", required = true) private String toUserName; @XmlElement(name = "FromUserName", required = true) private String fromUserName; @XmlElement(name = "CreateTime", required = true) private Long createTime; @JSONField(name = "msgtype") @XmlElement(name = "MsgType", required = true) private String msgType; @XmlElement(name = "Content", required = false) private String content; @JSONField(name = "text") private Text sendContent; @XmlElement(name = "ArticleCount", required = true) private Integer articleCount; @XmlElementWrapper(name = "Articles") @XmlElement(name = "item", required = false) private List<Article> Articles; @JSONField(name = "news") private ArticlesDto articlesDto; @JSONField(name = "image") @XmlElement(name = "Image", required = false) private Image image; @JSONField(name = "voice") @XmlElement(name = "Voice", required = false) private Voice voice; @JSONField(name = "video") @XmlElement(name = "Video", required = false) private Video video; @JSONField(name = "music") @XmlElement(name = "Music", required = false) private Music music; }
回覆文字訊息Text
@Data public class Text { @JSONField(name = "content") private String content; }
回覆圖文訊息Article
@Data @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "xml") public class Article { @JSONField(name = "title") @XmlElement(name = "Title", required = true) private String title; @JSONField(name = "description") @XmlElement(name = "Description", required = true) private String Description; @JSONField(name = "picurl") @XmlElement(name = "PicUrl", required = true) private String PicUrl; @JSONField(name = "url") @XmlElement(name = "Url", required = true) private String Url; }
回覆圖片訊息Image
@Data @XmlAccessorType(XmlAccessType.FIELD) public class Image { @JSONField(name = "media_id") @XmlElement(name = "MediaId", required = true) private String mediaId; }
回覆語音訊息Voice
@Data @XmlAccessorType(XmlAccessType.FIELD) public class Voice { @JSONField(name = "media_id") @XmlElement(name = "MediaId", required = true) private String mediaId; }
回覆視訊訊息Video
@Data @XmlAccessorType(XmlAccessType.FIELD) public class Video { @JSONField(name = "media_id") @XmlElement(name = "MediaId", required = true) private String mediaId; @JSONField(name = "title") @XmlElement(name = "Title") private String title; @JSONField(name = "description") @XmlElement(name = "Description") private String description; @JSONField(name = "thumb_media_id") private String thumbMediaId; }
回覆音樂訊息Music
@Data @XmlAccessorType(XmlAccessType.FIELD) public class Music { @JSONField(name = "title") @XmlElement(name = "Title") private String title; @JSONField(name = "description") @XmlElement(name = "Description") private String description; @JSONField(name = "musicurl") @XmlElement(name = "MusicURL") private String musicUrl; @JSONField(name = "hqmusicurl") @XmlElement(name = "HQMusicUrl") private String hQMusicUrl; @JSONField(name = "thumb_media_id") @XmlElement(name = "ThumbMediaId", required = true) private String thumbMediaId; }
訊息XML解析工具類XMLParse
package com.gaoxiaobo.download.utils; import com.gaoxiaobo.download.exception.AesException; import org.dom4j.DocumentException; import org.dom4j.io.SAXReader; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import javax.servlet.http.HttpServletRequest; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.*; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Auther: gaoxiaobo * @Date: 2020/8/3 21:35 * @Description: */ public 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(); DocumentBuilder 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"); NodeList nodelist2 = root.getElementsByTagName("ToUserName"); result[0] = 0; result[1] = nodelist1.item(0).getTextContent(); result[2] = nodelist2.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 toUser, String timestamp, String nonce) { String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n" + "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" + "<ToUserName><![CDATA[%3$s]]></ToUserName>" + "<TimeStamp>%4$s</TimeStamp>\n" + "<Nonce><![CDATA[%5$s]]></Nonce>\n" + "</xml>"; return String.format(format, encrypt, signature, toUser, timestamp, nonce); } /** * 將Map轉換為XML格式的字串 * * @param data Map型別資料 * @return XML格式的字串 * @throws Exception */ public static String mapToXml(Map<String, Object> data) throws Exception { try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder(); org.w3c.dom.Document document = documentBuilder.newDocument(); org.w3c.dom.Element root = document.createElement("xml"); document.appendChild(root); for (String key: data.keySet()) { String value = data.get(key) == null ? null : data.get(key).toString(); if (value == null) { value = ""; } value = value.trim(); org.w3c.dom.Element filed = document.createElement(key); filed.appendChild(document.createTextNode(value)); root.appendChild(filed); } TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source = new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", ""); writer.close(); return output; } catch (Exception e) { e.printStackTrace(); return null; } } /** * XML格式字串轉換為Map * * @param xml XML字串 * @return XML資料轉換後的Map * @throws Exception */ public static Map<String, String> xmlToMap(String xml) { try { Map<String, String> data = new HashMap<>(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream stream = new ByteArrayInputStream(xml.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList = doc.getDocumentElement().getChildNodes(); for (int idx = 0; idx < nodeList.getLength(); ++idx) { Node node = nodeList.item(idx); if (node.getNodeType() == Node.ELEMENT_NODE) { org.w3c.dom.Element element = (org.w3c.dom.Element) node; data.put(element.getNodeName(), element.getTextContent()); } } stream.close(); return data; } catch (Exception e) { e.printStackTrace(); return null; } } @SuppressWarnings("unchecked") public static<T> T xmlToBean(InputStream is,Class<T> c){ T t = null; try { JAXBContext context = JAXBContext.newInstance(c); Unmarshaller unmarshaller = context.createUnmarshaller(); t = (T)unmarshaller.unmarshal(is); } catch (Exception e) { e.printStackTrace(); } return t; } @SuppressWarnings("unchecked") public static <T> T xmlToBean(String xml, Class<T> beanClass)throws Exception{ JAXBContext context = JAXBContext.newInstance(beanClass); Unmarshaller unmarshaller = context.createUnmarshaller(); return (T) unmarshaller.unmarshal(new StringReader(xml)); } public static String beanToXML(Object bean)throws Exception{ JAXBContext context = JAXBContext.newInstance(bean.getClass()); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); StringWriter writer = new StringWriter(); marshaller.marshal(bean, writer); return writer.toString(); } public static Map<String, String> xmlToMap(HttpServletRequest request) { Map<String, String> map = new HashMap<String, String>(); SAXReader reader = new SAXReader(); InputStream ins = null; try { ins = request.getInputStream(); } catch (IOException e1) { e1.printStackTrace(); } org.dom4j.Document doc = null; try { doc = reader.read(ins); } catch (DocumentException e1) { e1.printStackTrace(); } org.dom4j.Element root = doc.getRootElement(); @SuppressWarnings("unchecked") List<org.dom4j.Element> list = root.elements(); for (org.dom4j.Element e : list) { map.put(e.getName(), e.getText()); } try { ins.close(); } catch (IOException e1) { e1.printStackTrace(); } return map; } }
模板訊息封裝TemplateMessage
package com.gaoxiaobo.download.component.wx.message; import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; import java.util.Map; @Data public class TemplateMessage { private String touser; @JSONField(name = "template_id") private String templateId; private String url; private String topcolor; private Map<String, TemplateData> data; }
package com.gaoxiaobo.download.component.wx.message; import lombok.Data; @Data public class TemplateData { private String color; private String value; }