微信被动回复消息,微信会调用我们自己的服务,传递一些敏感数据,为了确保安全性,可以给参数加密,当然,如果微信传的参数加密了,我们获取数据的时候自然需要解密,那么加解密需要怎么做呢?
如下图:我们在公众号基础配置里,将消息加密方式设置成「安全模式」之后,传递的就是加密之后的消息。
微信公众号平台:https://mp.weixin.qq.com/
在公众号开发文档里面,可以找到「消息加解密说明」并且附上了代码下载位置
公众号开发文档:
https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Message_encryption_and_decryption_instructions.html
代码压缩包下载下来
根据自己的开发语言选择对应代码
解压之后,我的开发语言是Java,然后把src里的文件复制到我们自己的项目里
XMLParse.java 和WxInMsgBean.java 是自己定义的工具类和实体类
可能需要引入信息jar包,在pom.xml里配置好即可
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.6.1version>
dependency>
<dependency>
<groupId>com.thoughtworks.xstreamgroupId>
<artifactId>xstreamartifactId>
<version>1.4.17version>
dependency>
接收微信回调推送消息Controller
/**
* 接收微信推送事件
*/
@RequestMapping(value = "/checksignature", method = RequestMethod.POST, produces = {"application/xml; charset=UTF-8"})
@ResponseBody
public void handleWxEvent(@RequestBody WxInMsgBean bean,
@RequestParam(required = false) String signature,
@RequestParam(required = false) String timestamp,
@RequestParam(required = false) String nonce,
@RequestParam(name = "encrypt_type", required = false) String encryptType,
@RequestParam(name = "msg_signature", required = false) String msgSignature,
HttpServletResponse response) {
log.info("--------------------接收微信推送事件-----------------------");
response.setCharacterEncoding("UTF-8");
wxService.handleEvent(bean, signature, timestamp, nonce, encryptType, msgSignature, response);
}
public interface IWxService {
/**
* 微信扫码触发事件
*
* @param msgBean 微信用户信息
* @param signature 签名
* @param timestamp 时间戳
* @param nonce 随机串
* @param encryptType 加密方式
* @param msgSignature 加密签名
* @param response response
*/
void handleEvent(WxInMsgBean msgBean, String signature, String timestamp, String nonce, String encryptType, String msgSignature, HttpServletResponse response);
}
@Service
@Slf4j
public class WxServiceImpl implements IWxService {
Logger logger = LoggerFactory.getLogger(WxServiceImpl.class);
@Value("${wx.appid:''}")
private String appid;
@Value("${wx.secret:''}")
private String secret;
@Value("${kol.domainname:''}")
private String domainname;
@Override
@Transactional(rollbackFor = Exception.class)
public void handleEvent(WxInMsgBean bean, String signature, String timestamp, String nonce, String encryptType, String msgSignature, HttpServletResponse response) {
log.info("----------- signature:{},timestamp:{},nonce:{},encryptType:{},msgSignature:{}", signature, timestamp, nonce, encryptType, msgSignature);
WxInMsgBean wxInMsgBean = new WxInMsgBean();
String result = "";
WXBizMsgCrypt crypt = null;
try {
crypt = new WXBizMsgCrypt("公众号里配置的token", "公众号里配的EncodingAESKey", appid);
String format = " ";
String fromXml = String.format(format, bean.getEncrypt());
// 解密收到的消息
result = crypt.decryptMsg(msgSignature, timestamp, nonce, fromXml);
log.info("解密后明文: " + result);
wxInMsgBean = XmlUtils.xmlToBean(result, WxInMsgBean.class);
} catch (Exception e) {
log.error("解密异常:", e);
}
try {
// openId
String userOpenId = wxInMsgBean.getFromUserName();
// 微信账号
String userName = wxInMsgBean.getToUserName();
// 事件
String event = wxInMsgBean.getEvent();
// 区分消息类型
String msgType = wxInMsgBean.getMsgType();
// ticket凭证
String ticket = wxInMsgBean.getTicket();
// 普通消息
if ("text".equals(msgType)) {
// todo 处理文本消息
}
// 事件推送消息
else if ("event".equals(msgType)) {
String mapToXml = handleMsgAndLog(userOpenId, userName, null);
// 消息加密
String str = crypt.encryptMsg(mapToXml, timestamp, nonce);
log.info("用户扫码|关注|加密后: " + str);
response.getWriter().print(str);
}
} catch (Exception e) {
logger.error("处理微信公众号请求异常:", e);
}
}
/**
* 处理消息
*/
private String handleMsgAndLog(String fromUserName, String toUserName, AdvWxUser wxUser) {
String mapToXml = getReturnMsgScan(fromUserName, toUserName);
return mapToXml;
}
/**
* 消息赋值
*/
public String getReturnMsgScan(String fromUserName, String toUserName) {
TextMessage textMessage = new TextMessage();
textMessage.setToUserName(fromUserName);
textMessage.setFromUserName(toUserName);
textMessage.setCreateTime(System.currentTimeMillis());
textMessage.setMsgType("text");
textMessage.setContent("欢迎回来...\n" +
"\n" +
"通过PC端登录网站\n" + domainname +
"\n" +
"\n" +
"助您玩转投资");
return getXmlString(textMessage);
}
/**
* 拼接XML文件
*/
public String getXmlString(TextMessage textMessage) {
String xml = "";
if (textMessage != null) {
xml = "" ;
xml += ";
xml += textMessage.getToUserName();
xml += "]]> ";
xml += ";
xml += textMessage.getFromUserName();
xml += "]]> ";
xml += "" ;
xml += textMessage.getCreateTime();
xml += "";
xml += ";
xml += textMessage.getMsgType();
xml += "]]> ";
xml += ";
xml += textMessage.getContent();
xml += "]]> ";
xml += "";
}
return xml;
}
实体类
import lombok.Data;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
/**
* 微信公众号信息接入参数 bean
*/
@Data
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WxInMsgBean implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 开发者微信号
*/
private String ToUserName;
/**
* 发送方帐号(一个OpenID)
*/
private String FromUserName;
/**
* 消息创建时间 (整型)
*/
private Long CreateTime;
/**
* 消息类型: MSG_TYPE_*
*/
private String MsgType;
/**
* 消息id,64位整型
*/
private Long MsgId;
/**
* 文本消息内容
*/
private String Content;
/**
* 图片链接(由系统生成)
*/
private String PicUrl;
/**
* 图片消息媒体id,可以调用获取临时素材接口拉取数据。
* 语音消息媒体id,可以调用获取临时素材接口拉取数据。
* 视频消息媒体id,可以调用获取临时素材接口拉取数据。
*/
private String MediaId;
/**
* 语音格式,如amr,speex等
*/
private String Format;
/**
* 语音识别结果,UTF8编码
* 需开通语音识别
*/
private String Recognition;
/**
* 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
*/
private String ThumbMediaId;
/**
* 地理位置消息:地理位置纬度
*/
private String Location_X;
/**
* 地理位置消息:地理位置经度
*/
private String Location_Y;
/**
* 地理位置消息:地图缩放大小
*/
private String Scale;
/**
* 地理位置消息:地理位置信息
*/
private String Label;
/**
* 链接消息:消息标题
*/
private String Title;
/**
* 链接消息:消息描述
*/
private String Description;
/**
* 链接消息:消息链接
*/
private String Url;
/**
* 事件类型 EVENT_TYPE_*
*/
private String Event;
/**
* 扫描带参数二维码事件:用户未关注时,进行关注后的事件推送:事件KEY值,qrscene_为前缀,后面为二维码的参数值
* 扫描带参数二维码事件:用户已关注时的事件推送:事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
* 自定义菜单事件:点击菜单拉取消息时的事件推送:事件KEY值,与自定义菜单接口中KEY值对应
* 自定义菜单事件:点击菜单跳转链接时的事件推送:事件KEY值,设置的跳转URL
*/
private String EventKey;
/**
* 扫描带参数二维码事件:二维码的ticket,可用来换取二维码图片
*/
private String Ticket;
/**
* 上报地理位置事件:地理位置纬度
*/
private String Latitude;
/**
* 上报地理位置事件:地理位置经度
*/
private String Longitude;
/**
* 上报地理位置事件:地理位置精度
*/
private String Precision;
/**
* aes 加密后的文本信息
*/
private String Encrypt;
/**
* 解密后携带的访问路径
*/
private String URL;
/**
* 微信公众号发送模板消息给用户的发送状态
*/
private String Status;
}
工具类 XMLUtils
import cn.hutool.core.util.StrUtil;
import com.thoughtworks.xstream.XStream;
/**
* xml 相关工具类
*
*/
public class XmlUtils {
/**
* 将 Java Bean 转化为 XML
*
* @param bean {@link Object}
* @param cls 传入对象的字节码
* @return XML 字符串
*/
public static <T> String beanToXml(Object bean, Class<T> cls) {
XStream stream = new XStream();
stream.processAnnotations(cls);
return stream.toXML(bean);
}
/**
* 将 Java Bean 转化为 XML
*
* @param bean {@link Object}
* @return XML 字符串
*/
public static String beanToXml(Object bean) {
return beanToXml(bean, bean.getClass());
}
/**
* 将 xml 转化为 Bean
*
* @param xml xml
* @param cls bean 的类型
* @param alias 对应 bean 的别名
* @param 泛型
* @return T
*/
@SuppressWarnings("unchecked")
public static <T> T xmlToBean(String xml, Class<T> cls, String alias) {
if (StrUtil.hasBlank(xml, alias)) {
throw new IllegalArgumentException("xml to Bean 参数错误");
}
XStream stream = new XStream();
stream.alias(alias, cls);
return (T) stream.fromXML(xml);
}
/**
* 将 xml 转化为 Bean,bean 默认别名为 xml
*
* @param xml xml
* @param cls bean 的类型
* @param 泛型
* @return T
*/
public static <T> T xmlToBean(String xml, Class<T> cls) {
return xmlToBean(xml, cls, "xml");
}
}
代码里写完加解密之后,微信公众号基础配置 信息就不要随便改了,「兼容模式」和「安全模式」都可以支持此加解密。