1. 程式人生 > 實用技巧 >微信公眾號訊息、加解密、模板訊息封裝

微信公眾號訊息、加解密、模板訊息封裝

轉自我的個人部落格:微信公眾號訊息、加解密、模板訊息封裝

最近又在做個公眾號相關的系統,之前寫公眾號經常動不動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;
}