您现在的位置是:首页 > 技术人生 > 后端技术后端技术
微信公众号消息、加解密、模板消息封装
高晓波2020-08-13【后端技术】人已围观
简介最近又在做个公众号相关的系统,之前写公众号经常动不动Map,这次实在看不下去了,基本都进行了封装。
以此文记录,一来以后复用,二来也是希望能帮助到有需要的人。
一、加、解密
最近又在做个公众号相关的系统,之前写公众号经常动不动Map,这次实在看不下去了,基本都进行了封装。
以此文记录,一来以后复用,二来也是希望能帮助到有需要的人。
回复文本消息Text
回复图文消息Article
回复图片消息Image
回复视频消息Video
回复音乐消息Music
消息XML解析工具类XMLParse
模板消息封装TemplateMessage
以此文记录,一来以后复用,二来也是希望能帮助到有需要的人。
一、加、解密工具类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;
}
Tags:公众号
很赞哦! ()
随机图文
-
mybatis plus生成实体entity没有id
总是写很多重复的增删改查毫无意义,浪费生命,于是乎准备弄一个增删改查代码生成工具。网上查了一下mybatis plus挺好的,于是乎打开官网 Quick Start!按照官网的演示栗子:// 演示 -
maven安装jar到本地仓库
JAVA做淘宝客程序开发的时候,我用的是maven管理依赖jar包。 如何将阿里妈妈下载的jar包以及source jar安装到maven本地仓库呢? -
SpringBoot排除自动配置
SpringBoot的自动配置给我们开发带来了极大的便利,但有些时候也带来了一些问题。 问题场景: 该项目是基于Springboot + dubbo的微服务架构,框架结构web + facade + service,某 -
Springboot集成quartz定时任务可视化配置
使用quartz定时任务已经有一段时间了,今天记录一下Springboot 2.x集成Quartz。