1.1、内网转发
1)进入Sunny-Ngrok官方网站(https://www.ngrok.cc/),注册自己的账户,并登陆。
2)登陆后,在右侧会有一个隧道管理,新用户选择开通隧道。
3)在进到开通隧道后,会出现如图所示,我们选择第三个“香港Ngrok免费服务器”。
4)在进行购买后,我们会进入到隧道信息的配置页面,在这里,我们将对我们的隧道信息进行配置。
配置说明:
①、隧道协议选择“http”协议;
②、隧道名称自定义;
③、前置域名要求纯数字或者字母,不可以是汉字或其他非法字符
④、本地端口,默认为80口,修改为8080端口(不进行修改也可以但必须是80端口或者443端口,因为微信不开放其他的端口。)
5)在配置好后,确定添加,后续操作直接默认。最后,回到隧道管理,我们会看到我们的服务器已经搭建好了。将隧道ID复制下来。
6)下载sunny-Ngrok客户端,选择自己的电脑操作系统版本,下载适合自己的客户端。
7)打开我们下载的文件,找到“Sunny-Ngrok启动工具.bat”文件,双击打开。将我们的隧道ID复制上去,回车。当出现图所示的界面是,我们内网转发部分就完成了。复制我们的公网地址。






一、 准备工作
1.2 微信公众号
1)微信公众号的搭建,请自行在网上找相关教程,本文档只考虑已拥有微信公众号平台。
2)在搭建好微信公号后,进入。我们可以看到如图所示的界面。
3)在图中右侧,点击“自动回复”,在默认状态下,我们自动回复功能是开放的,我们开通“开发者模式”后,需要对其进行关闭。
4)在确保微信公众号开通“开发者模式”后,点击右侧的“基本配置”,我们会看到如图所示的界面。URL填写规范为“服务器地址+代码位置”,具体配置参照微信开发文档。提交后,启动配置。此时,微信端的配置就结束了。
接下来,就是我们的托管代码部分。
二、 托管代码
2.1、Message类——所有的微信消息的方法类
public class Message {
// 发送微信号
private String ToUserName ;
// 开发人员
private String FromUserName;
// 消息创建时间 (整型)
private long CreateTime;
// 消息类型(text/image/location/link)
private String MsgType;
// 消息id,64位整型
private String MsgId;
//消息内容
private String Content ;
public Message(){
}
public String getToUserName() {
return ToUserName;
}
public void setToUserName(String toUserName) {
ToUserName = toUserName;
}
public String getFromUserName() {
return FromUserName;
}
public void setFromUserName(String fromUserName) {
FromUserName = fromUserName;
}
public long getCreateTime() {
return CreateTime;
}
public void setCreateTime(long createTime) {
CreateTime = createTime;
}
public String getMsgType() {
return MsgType;
}
public void setMsgType(String msgType) {
MsgType = msgType;
}
public String getMsgId() {
return MsgId;
}
public void setMsgId(String msgId) {
MsgId = msgId;
}
public String getContent() {
return Content;
}
public void setContent(String content) {
Content = content;
}
}
2.2、MessageType类——消息的类型
public class MessageType {
//文本
public static final String TEXT = "text" ;
public static final String EVENT = "event" ;
//关注
public static final String SUBSCRIBE = "subcribe" ;
public static final String UNSUBSCRIBE = "unsubcribe" ;
public static final String IMAGE = "image" ;
public static final String VOICE = "voice" ;
public static final String VIDEO = "video" ;
public static final String SHORTVIDEO = "shortvideo" ;
public static final String LOCATION = "location" ;
public static final String LINK = "link" ;
//菜单单击
public static final String CLICK = "click" ;
public static final String VIEN = "vien" ;
}
2.3、MessageUntil类——消息处理
importjava.io.IOException;
importjava.io.InputStream;
importjava.util.HashMap;
importjava.util.List;
importjava.util.Map;
importorg.dom4j.Document;
importorg.dom4j.DocumentException;
import org.dom4j.Element;
importorg.dom4j.io.SAXReader;
importcom.thoughtworks.xstream.XStream;
public class MessageUntil {
public static MaptoMap(InputStream is) throwsDocumentException, IOException {
//获取SaxReader
SAXReader reader = new SAXReader();
//获取文档
Document document = reader.read(is) ;
//获取跟元素
Element root = document.getRootElement();
//获取其他元素
@SuppressWarnings("unchecked")
List<<span style="background:lightgrey; mso-highlight:lightgrey">Element> elements = root.elements() ;
//遍历封装到Map中
Map map = new HashMap();
for(Element e:elements){
map.put(e.getName(), e.getText()) ;
}
is.close();
return map;
}
public static Message toMessage(Map map){
Message message = new Message();
message.setMsgType(map.get("MsgType"));
message.setContent(map.get("Content"));
message.setCreateTime(Integer.valueOf(map.get("CreateTime")));
message.setFromUserName(map.get("FromUserName"));
message.setMsgId(map.get("MsgId"));
message.setToUserName(map.get("ToUserName"));
return message;
}
public static String toXml(Message message) {
// TODO Auto-generated method stub
XStream stream = new XStream();
//使用别名,作为跟元素,否则跟元素为包名,类名
//
//asdf
//
stream.alias("xml", message.getClass());
return stream.toXML(message);
}
}
2.4、SendMessage类——发送消息
修改意见:本发送代码实在每一个发送中均建立了对数据库的连接,造成了资源的浪费。使用者可将数据库写成方法类,进行直接调用,或者通过连接池进行连接,减少资源的浪费。
importjava.sql.DriverManager;
import java.sql.PreparedStatement;
importjava.sql.ResultSet;
import java.sql.SQLException;
importjava.sql.Statement;
importjava.sql.Connection;
import com.MySqlManage.MyDBManger;
public class SendMessage {
Connection connection = null ;
ResultSet rs = null ;
Statement statement = null ;
public String other(String content) {
// TODO Auto-generated method stub
StringBuffer buffer = new StringBuffer();
buffer.append("*家庭安全智能报警系统*\n") ;
buffer.append("提醒您,您输入的信息系统无法识别,请输入 “ 1 ” ,进入主页面 \n") ;
return "您发送的信息为: " + content + "\t\n"+ buffer.toString();
}
public String sendTextAuto() {
// TODO Auto-generated method stub
StringBuffer buffer = new StringBuffer();
buffer.append("***************\n");
buffer.append("*家庭安全智能报警系统*\n") ;
buffer.append("****WELCOME****\n");
buffer.append("***1、主页面 \n");
buffer.append("***2、查询历史温度数据 \n");
buffer.append("***3、查询历史湿度数据 \n");
buffer.append("***4、查询历史co浓度数据 \n");
return buffer.toString();
}
public String sendTextTemp() {
// TODO Auto-generated method stub
StringBuffer buffer = new StringBuffer();
buffer.append("*家庭安全智能报警系统*\n") ;
buffer.append("*****温度 ******\n") ;
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("成功加载MySQL驱动!");
String url="jdbc:mysql://localhost:3306/myautohome";
connection = DriverManager.getConnection(url,"root","");
//connection = MyDBManger.getDBManger().getconnection();
statement = connection.createStatement();
rs = statement.executeQuery("SELECT ID,TEMPTURE FROM autohome ");
buffer.append(
"编号" + "\t"+ "\t" + "温度"+ "\t" + "\n"
);
while(rs.next()) {
buffer.append(
rs.getString("ID") + "\t " +
rs.getString("TEMPTURE")+ "\t" +"\n"
);
}
connection.close();
rs.close();
statement.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return buffer.toString();
}
public String sendTextHumi() {
// TODO Auto-generated method stub
StringBuffer buffer = new StringBuffer();
buffer.append("*家庭安全智能报警系统*\n") ;
buffer.append("******湿度 *****\n") ;
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("成功加载MySQL驱动!");
String url="jdbc:mysql://localhost:3306/myautohome";
connection = DriverManager.getConnection(url,"root","");
statement = connection.createStatement();
rs = statement.executeQuery("SELECT ID,HUMITURE FROM autohome ");
buffer.append(
"编号 " + "\t"+ "\t" +"湿度"+ "\n"
);
while(rs.next()) {
buffer.append(
rs.getString("ID") + "\t "+
rs.getString("HUMITURE")+ "\t" +"\n"
);
}
connection.close();
rs.close();
statement.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return buffer.toString();
}
public String sendTextCo() {
// TODO Auto-generated method stub
StringBuffer buffer = new StringBuffer();
buffer.append("*家庭安全智能报警系统*\n") ;
buffer.append("****co浓度 ******\n") ;
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("成功加载MySQL驱动!");
String url="jdbc:mysql://localhost:3306/myautohome";
connection = DriverManager.getConnection(url,"root","");
statement = connection.createStatement();
rs = statement.executeQuery("SELECT ID,CO FROM autohome ");
buffer.append(
"编号" + "\t"+ "\t" +"CO浓度"+ "\t" + "\n"
);
while(rs.next()) {
buffer.append(
rs.getString("ID") + "\t "+
rs.getString("CO")+ "\t" +"\n"
);
}
connection.close();
rs.close();
statement.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return buffer.toString();
}
}
2.5、WxServlet类——主类
importjava.util.Collections;
importjava.util.List;
importjava.util.Map;
importjava.io.IOException;
importjava.io.PrintWriter;
importjava.util.ArrayList;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.apache.commons.codec.digest.DigestUtils;
importorg.dom4j.DocumentException;
public class WxServlet extends HttpServlet {
@Override
protected voiddoGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
//设置字符集 utf-8
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
//获取输出
PrintWriter out = resp.getWriter();//响应
//开始接入
connect(req,out);
}
//接受数据
@Override
protected voiddoPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
//设置字符集 utf-8
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
//回送信息
echoMsg(req,resp);
}
public void echoMsg(HttpServletRequest req, HttpServletResponse resp) throwsIOException {
// TODO Auto-generated method stub
PrintWriter writer = resp.getWriter();
Map xmlMap = null ;
SendMessage SendContent = new SendMessage() ;
try {
//XML -> 解析 -> 存入Map中 -> XML数据
xmlMap = MessageUntil.toMap(req.getInputStream());
Message message = MessageUntil.toMessage(xmlMap);
String from = message.getFromUserName();
String to = message.getToUserName() ;
message.setFromUserName(to);
message.setToUserName(from);
message.setCreateTime(System.currentTimeMillis());
//判断内容是否为空
String content = message.getContent() ;
content = null == content?"":content.trim();
//书写返回数据
String reply = "" ;
//获取消息类型
String type = xmlMap.get("MsgType");
//比较
if(type.equals(MessageType.TEXT)){
if(content.equals("1")||content.equalsIgnoreCase("AutoHome")){
reply = SendContent.sendTextAuto();
}else if(content.equals("2")||content.equalsIgnoreCase("Tempture")){
reply = SendContent.sendTextTemp();
}else if(content.equals("3")||content.equalsIgnoreCase("Humiture")){
reply = SendContent.sendTextHumi();
}else if(content.equals("4")||content.equalsIgnoreCase("Co")){
reply = SendContent.sendTextCo();
}
else{
reply = SendContent.other(content);
}
}else if(type.equals(MessageType.EVENT)){
String eventType = xmlMap.get("Event").toLowerCase();//忽略大小写
if(eventType.equals(MessageType.SUBSCRIBE)){
reply = SendContent.sendTextAuto();
message.setMsgType(MessageType.TEXT);
}else if(eventType.equals(MessageType.UNSUBSCRIBE)){
System.out.println("推出了");
message.setMsgType(MessageType.TEXT);
}
}
//设置回复的信息
message.setContent(reply);
//将message对新疆转化为xml格式
String xmlStr = MessageUntil.toXml(message);
System.out.println(xmlStr);
//使用writer对象发送
writer.print(xmlStr);
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void connect(HttpServletRequest req, PrintWriter out) {
// TODO Auto-generated method stub
//获取参数
String signature = req.getParameter("signature");//微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
String timestamp = req.getParameter("timestamp");//时间戳
String nonce = req.getParameter("nonce");//随机数
String echostr = req.getParameter("echostr");//随机字符串
//校验
List list = new ArrayList();
list.add("AutoHome");
list.add(timestamp);
list.add(nonce);
//开始排序
Collections.sort(list);
//拼接字符串 --》进行加密
StringBuffer buffer = new StringBuffer();
for(String string : list ){
buffer.append(string);
}
//使用apache commons codec 进行加密
String shalStr = DigestUtils.shaHex(buffer.toString());
//做对比
boolean flag = shalStr.equals(signature) ;
if(flag){
System.out.println("接入成功");
out.print(echostr);
out.flush();
}
}
}
三、 常见问题
3.1、URL请求超时
1)服务器地址配置是否出现问题;
2)Sunny-Ngrok是否被关闭;
3.2、Tokey错误
1)检查代码中的Tokey与微信公众平台中的Tokey配置是否相同;
2)检查Tokey配置是否正确;
3.3、测试时,出现“该微信公众号服务器异常,请稍后再试”
检查MessageUntil类中,tomessage和toxml方法是否正常。
3.4、说明
本代码亲测可用,若在使用过程中出现其他问题,可加QQ:2292367539 进行讨论。