本教程按我自己方式写的,不一定对,但是是可以实现的,为了方便逻辑都写在了Controller层,我数据是静态的,可以自行用数据库动态生成。
内网穿透
微信官方文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html
当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。
请注意:
1,关于重试的消息排重,推荐使用msgid排重。
2,微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。详情请见“发送消息-被动回复消息”。
3,如果开发者需要对用户消息在5秒内立即做出回应,即使用“发送消息-被动回复消息”接口向用户被动回复消息时,可以在公众平台官网的开发者中心处设置消息加密。开启加密后,用户发来的消息和开发者回复的消息都会被加密(但开发者通过客服接口等API调用形式向用户发送消息,则不受影响)。关于消息加解密的详细说明,请见“发送消息-被动回复消息加解密说明”。 各消息类型的推送XML数据包结构如下:
文本消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
Controller-------代码注释详细,不做多解释
@RestController
@RequestMapping("/a")
public class TemplateController {
private static final Logger logger = Logger.getLogger(TemplateController.class);
private static final org.slf4j.Logger logger2 = LoggerFactory.getLogger(TemplateController.class);
//url校验
@RequestMapping(value = "/c", method = RequestMethod.GET)
public String weiXinGet(HttpServletRequest req, HttpServletResponse resp) throws Exception {
logger.info("微信请求接入>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//获取微信加密签名
String signature = req.getParameter("signature");
logger2.info("微信签名是: {}", signature);
//获取时间戳
String timestamp = req.getParameter("timestamp");
//获取随机数
String nonce = req.getParameter("nonce");
//获取随机字符串
String echostr = req.getParameter("echostr");
//判断加密后的字符串和签名是否一样.如果一样表示接入成功
if (InspectionUtil.checkSignature(signature, timestamp, nonce)) {
logger.info("已成功发送———————" + echostr);
return echostr;
}
return null;
}
//消息处理
@RequestMapping(value = "/c", method = RequestMethod.POST)
public String weiXinPost(@RequestBody InWeiXinMessage weiXinMessage) throws Exception {
OutWeiXinMessage outWeiXinMessage = new OutWeiXinMessage();
//发送方
outWeiXinMessage.setFromUserName(weiXinMessage.getToUserName());
//接受方
outWeiXinMessage.setToUserName(weiXinMessage.getFromUserName());
//str = weiXinMessage.getFromUserName();
//消息创建时间
outWeiXinMessage.setCreateTime(new Date().getTime());
//消息类型
outWeiXinMessage.setMsgType("text");
System.out.println(weiXinMessage.getContent());
//消息内容
String content = weiXinMessage.getContent();
switch (content) {
case "一级菜单<1>":
outWeiXinMessage.setContent("<a href=\"weixin://bizmsgmenu?msgmenucontent=二级菜单<1>&msgmenuid=102\">二级菜单<1></a>\n" +
"<a href=\"weixin://bizmsgmenu?msgmenucontent=二级菜单<2>&msgmenuid=103\">二级菜单<2></a>\n" +
"<a href=\"weixin://bizmsgmenu?msgmenucontent=二级菜单<3>&msgmenuid=104\">二级菜单<3></a>");
break;
case "二级菜单<1>":
outWeiXinMessage.setContent("二级菜单<1>");
break;
case "一级菜单<2>":
outWeiXinMessage.setContent("我是一级菜单<2>");
break;
case "一级菜单<3>":
outWeiXinMessage.setContent("我是一级菜单<3>");
break;
default:
outWeiXinMessage.setContent("<a href=\"weixin://bizmsgmenu?msgmenucontent=一级菜单<1>&msgmenuid=102\">一级菜单<1></a>\n" +
"<a href=\"weixin://bizmsgmenu?msgmenucontent=一级菜单<2>&msgmenuid=103\">一级菜单<2></a>\n" +
"<a href=\"weixin://bizmsgmenu?msgmenucontent=一级菜单<3>&msgmenuid=104\">一级菜单<3></a>");
}
XStream xStream = new XStream();
//设置别名
xStream.alias("xml", OutWeiXinMessage.class);
String xml = xStream.toXML(outWeiXinMessage);
return xml;
}
}
XStream的pom.xml,其他常用的不做多阐述
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.9</version>
</dependency>
InspectionUtil
public class InspectionUtil {
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] strArrays = {Constast.Token, timestamp, nonce};
Arrays.sort(strArrays);
//拼接字符串
String str = new StringBuffer().append(strArrays[0]).append((strArrays[1])).append((strArrays[2])).toString();
//加密的字符串跟微信加密签名是否一样
return InspectionUtil.sha1(str).equals(signature);
}
//将字符串进行sha1加密
public static String sha1(String str) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(str.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 "";
}
}
InWeiXinMessage-----这里接收xml格式并转换,用的注解实现的
@Data
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class InWeiXinMessage {
@XmlElement(name = "ToUserName")
private String toUserName; //开发者微信号
private String FromUserName; //发送方帐号(一个OpenID)
private Long CreateTime; //消息创建时间 (整型)
private String MsgType; //消息类型,text/image/voice
private Long MsgId; //消息id,64位整型
private String Content; //文本消息内容
private String PicUrl; //图片链接(由系统生成)
private String MediaId; //语音消息媒体id,可以调用获取临时素材接口拉取数据。
}
OutWeiXinMessage-----这里本来想用注解实现,无法实现改用XStream
@Data
public class OutWeiXinMessage {
private String ToUserName; //用户微信号
private String FromUserName; //用户帐号(一个OpenID)
private Long CreateTime; //消息创建时间 (整型)
private String MsgType; //消息类型,text/image/voice
private String Content; //文本消息内容
}