想自己开一个公众号,先学习一下用 Java 进行微信公众号的开发。
微信公众号的管理有开发模式和编辑模式两种,两者是互斥的。
腾讯是这么讲的:
编辑模式:编辑模式指所有的公众号运营者都可以通过简单的编辑,设置“按关键字回复”等功能。您可以设定常用的文字/语言/图片/录音作为回复消息,并制定自动回复的规则。当订阅用户的行为符合自动回复规则的时候,就会收到自动回复的消息。
开发模式:开发模式是为开发者提供与用户进行消息交互的能力。对于成功接入消息接口的公众账号,当用户发消息给公众号,微信公众平台服务器会使用http请求对接入的网址进行消息推送,第三方服务器可通过响应包回复特定结构,从而达到回复消息的目的。
所以接下来要讲的是如果使用微信的开发模式。
微信的开发模式如果要调用他的接口需要微信认证300元。微信有公共平台测试号:
开发 — 开发者工具 — 公众号平台测试账号
微信的 URL 要求必须是: http:// 80端口,且必须是能够在公网访问的,本地的不行。所以这里用到一个映射工具叫做 ngrok 。下载之后使用,cmd
ngrok http 8080
准备完毕开始开发,包结构如下:
通过 servlet 的 GET 请求进行校验
微信需要对以上四个参数进行校验,将 timestamp,nonce,token 三者排序拼接成字符串,再进行 Sha1 加密之后,如果和 signature 相同则表示校验通过:
public static boolean checkSignature(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 加密 java实现消息摘要加密
String temp = getSha1(content.toString());
//和微信传递过来的参数进行校验
return temp.equals(signature);
}
sha1加密
public static String getSha1(String str) {
if (str == null || str.length() == 0) {
return null;
}
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f' };
try {
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char buf[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
} catch (Exception e) {
return null;
}
}
进行校验的servlet
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//验证消息的确来自微信服务器 校验四个参数
String signature = request.getParameter("signature"); //微信加密签名
String timestamp = request.getParameter("timestamp"); //时间戳
String nonce = request.getParameter("nonce"); //随机数
String echostr = request.getParameter("echostr"); //随机字符串
PrintWriter out = response.getWriter();
if(CheckUtil.checkSignature(signature, timestamp, nonce)){
out.print(echostr);
}
}
配置web.xml
<servlet>
<servlet-name>weixinServletservlet-name>
<servlet-class>com.shuiyujie.servlet.WeiXinServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>weixinServletservlet-name>
<url-pattern>/wx.dourl-pattern>
servlet-mapping>
访问是500,说明已经通了。接下来用 ngrok 映射工具生成映射地址,到公网上去访问看看行不行,这个在前面讲过不做演示。
在访问成功之后,对微信中的服务器配置进行填写提交。
可以提交并且启用开发模式了,启用了开发模式则编辑模式下的配置全部失效。
数据交互的过程如上图所示,通过 Post 请求进行。根据请求类型的不同,微信规定了不同的参数,并且通过 XML 的格式进行传递。通过查看开发文档,我们就可以了解所需要的参数。又由于是通过 XML 的格式传递的,为了便于后台程序的解析,所以将 XML 装换成集合类型。
通过微信文档可以知道,文本消息传递的参数列表如上图所示,所以要新建一个 Po 类。
public class TextMessage {
private String ToUserName;
private String FromUserName;
private long CreateTime;
private String MsgType;
private String Content;
private String MsgId;
添加 get, set方法
XML 转集合
public static Map xmlToMap(HttpServletRequest request) throws IOException, DocumentException{
Map map = new HashMap();
SAXReader reader = new SAXReader();
InputStream ins = request.getInputStream();
Document doc = reader.read(ins);
//获取根节点
Element root = doc.getRootElement();
List list = root.elements();
for(Element e : list){
map.put(e.getName(), e.getText());
}
ins.close();
return map;
}
文本信息转 XML
public static String textMessageToXml(TextMessage textMessage){
XStream xstream = new XStream();
xstream.alias("xml", textMessage.getClass());
return xstream.toXML(textMessage);
}
servlet doPost 接收响应
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
try {
Map<String,String> map = MessageUtil.xmlToMap(request);
String fromUserName = map.get("FromUserName");
String toUserName = map.get("ToUserName");
String msgType = map.get("MsgType");
String content = map.get("Content");
String message = null;
if("text".equals(msgType)){
TextMessage text = new TextMessage();
text.setFromUserName(toUserName); //原来的信息发送者,将变成信息接受者
text.setToUserName(fromUserName); //原理的接受者,变成发送者
text.setMsgType("text"); //表示消息的类型是text类型
text.setCreateTime(new Date().getTime());
text.setContent("您发送的信息是:" + content);
message = MessageUtil.textMessageToXml(text); //装换成 xml 格式发送给微信解析
System.out.println(message);
}
out.print(message);
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
接下来需要启动 tomcat 运行程序。
这样消息回复的效果已经完成了,最后来让他能够接收更多类型的消息。比如常见的一些:关注后的消息回复,取消关注之后的消息回复,还有关键字的消息回复
由于会接收多种消息类型,修改 MessageUtil 消息工具类,为了方便把常用的消息类型都定义成常量,并且将需要回复的消息,例如关注之后回复的消息,关键词回复的消息都定义好
定义常量
public class MessageUtil {
public static final String MESSAGE_TEXT = "text";
public static final String MESSAGE_NEWS = "news";
public static final String MESSAGE_IMAGE = "image";
public static final String MESSAGE_VOICE = "voice";
public static final String MESSAGE_MUSIC = "music";
public static final String MESSAGE_VIDEO = "video";
public static final String MESSAGE_LINK = "link";
public static final String MESSAGE_LOCATION = "location";
public static final String MESSAGE_EVNET = "event";
public static final String MESSAGE_SUBSCRIBE = "subscribe";
public static final String MESSAGE_UNSUBSCRIBE = "unsubscribe";
public static final String MESSAGE_CLICK = "CLICK";
public static final String MESSAGE_VIEW = "VIEW";
public static final String MESSAGE_SCANCODE= "scancode_push";
/**
* 拼接文本消息
* @param toUserName
* @param fromUserName
* @param content
* @return
*/
public static String initText(String toUserName,String fromUserName,String content){
TextMessage text = new TextMessage();
text.setFromUserName(toUserName);
text.setToUserName(fromUserName);
text.setMsgType(MessageUtil.MESSAGE_TEXT);
text.setCreateTime(new Date().getTime());
text.setContent(content);
return textMessageToXml(text);
}
/**
* 关注回复
* @return
*/
public static String menuText(){
StringBuffer sb = new StringBuffer();
sb.append("欢迎关注公众号弄浪的鱼:\n\n");
sb.append("1 回复关键字1\n");
sb.append("2 回复关键字2\n");
sb.append("3 回复关键字3\n\n");
sb.append("本公众号还在开发中,没啥能看的,别玩坏了哈~~");
return sb.toString();
}
servlet对接收到的消息进行判断并作出响应
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
try {
Map map = MessageUtil.xmlToMap(request);
String fromUserName = map.get("FromUserName");
String toUserName = map.get("ToUserName");
String msgType = map.get("MsgType");
String content = map.get("Content");
String message = null;
if (MessageUtil.MESSAGE_TEXT.equals(msgType)) {
if ("1".equals(content)) {
message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.firstMenu());
} else if ("2".equals(content)) {
message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.secondMenu());
} else if ("3".equals(content)) {
message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.threeMenu());
} else if ("?".equals(content) || "?".equals(content)) {
message = MessageUtil.menuText();
}
} else if (MessageUtil.MESSAGE_EVNET.equals(msgType)) {// 事件推送
String eventType = map.get("Event"); // 事件分成多种,分别判断处理
if (MessageUtil.MESSAGE_SUBSCRIBE.equals(eventType)) { // 这里先写一个关注之后的事件
message = MessageUtil.initText(toUserName, fromUserName, MessageUtil.menuText());
}
}
System.out.println(message);
out.print(message);
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
以上,关注回复和关键词回复成功。
最后再把微信程序部署到服务器上就可以正常访问了,下一篇就总结怎么部署到阿里云。