开发环境的准备

配置jdk和tomcat

集成开发环境idea或eclipse

下载安装外网映射花生壳或Sunny-Ngrok,软件不限

开发模式

开发模式的接入

  • 填写服务器配置

登录微信公众平台官网后,在公众平台官网的开发-基本设置页面,勾选协议成为开发者,点击“修改配置”按钮,填写服务器地址(URL)、Token和EncodingAESKey,其中URL是开发者用来接收微信消息和事件的接口URL。Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。

  • 验证消息的确来自微信服务器

开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:

参数 描述
signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp 时间戳
nonce 随机数
echostr 随机字符串

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.webchat.util;
import java.security.MessageDigest;
import java.util.Arrays;
public class WxService {
private static String token = "mywebchat";
public static boolean checkSignature(String signature, String timestamp, String nonce) {
//1.将token、timestamp、nonce三个参数进行字典序排序
String[] strs = new String[]{token, timestamp, nonce};
Arrays.sort(strs);

//2.将三个参数字符串拼接成一个字符串进行sha1加密
String str = strs[0]+strs[1]+strs[2];
String mysig = sha1(str);

//3.开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
return mysig.equals(signature);
}

//进行sha1算法加密
private static String sha1(String str){
try {
//获取一个加密对象
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
//加密
byte[] digest = messageDigest.digest(str.getBytes());
//处理加密结果
char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
StringBuilder sb = new StringBuilder();
//处理加密结果
for (byte b : digest){
sb.append(chars[(b>>4)&15]);
sb.append(chars[b&15]);
}
return sb.toString();
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 	package com.webchat.servlet;

import com.webchat.util.CheckUtil;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/mywebchat")
public class WeixinServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String signature = req.getParameter("signature");
String timestamp = req.getParameter("timestamp");
String nonce = req.getParameter("nonce");
String echostr = req.getParameter("echostr");

//校验验证请求
if (CheckUtil.checkSignature(signature, timestamp, nonce)){
System.out.println("接入成功!");
PrintWriter out = resp.getWriter();
//原样返回echostr
out.print(echostr);
out.flush();
out.close();
}else {
System.out.println("接入失败!");
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

}
}

接收消息

当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。

  • 在上方WeixinServlet的dopost方法中输入
1
2
3
4
5
6
7
8
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
request.setCharacterEncoding("utf8");
response.setCharacterEncoding("utf8");
//处理消息和事件推送
Map<String,String> requestMap = WxService.parseRequest(request);
System.out.println(requestMap);
}
  • 在上方的WxService里加入解析xml数据包的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//解析xml数据包
public static Map<String,String> parseRequest(HttpServletRequest request) {
// 将解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>();
SAXReader reader = new SAXReader();
try {
//读取输入流获取文档对象
InputStream inputStream = request.getInputStream();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList) {
map.put(e.getName(), e.getStringValue());
}
inputStream.close();
return map;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

消息的封装

  • 创建com.webchat.entity.message目录,并在目录下创建BaseMessage,class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
 	package com.webchat.entity.message;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import java.util.Map;

@XStreamAlias("xml")
public class BaseMessage {
@XStreamAlias("ToUserName")
private String toUserName;

@XStreamAlias("FromUserName")
private String fromUserName;

@XStreamAlias("CreateTime")
private String createTime;

@XStreamAlias("MsgType")
private String msgType;

public BaseMessage(Map<String,String> requestMap) {
this.toUserName = requestMap.get("FromUserName");
this.fromUserName = requestMap.get("ToUserName");
this.createTime = System.currentTimeMillis()/1000+"";
}

public String getToUserName() {
return toUserName;
}

public void setToUserName(String toUserName) {
this.toUserName = toUserName;
}

public String getFromUserName() {
return fromUserName;
}

public void setFromUserName(String fromUserName) {
this.fromUserName = fromUserName;
}

public String getCreateTime() {
return createTime;
}

public void setCreateTime(String createTime) {
this.createTime = createTime;
}

public String getMsgType() {
return msgType;
}

public void setMsgType(String msgType) {
this.msgType = msgType;
}

@Override
public String toString() {
return "BaseMessage{" +
"toUserName='" + toUserName + '\'' +
", fromUserName='" + fromUserName + '\'' +
", createTime='" + createTime + '\'' +
", msgType='" + msgType + '\'' +
'}';
}
}
  • 在com.webchat.entity.message目录下创建继承BaseMessage的ImageMessage类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 	package com.webchat.entity.message;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import java.util.Map;

@XStreamAlias("xml")
public class ImageMessage extends BaseMessage {
@XStreamAlias("MediaId")
private String mediaId;

public String getMediaId() {
return mediaId;
}

public void setMediaId(String mediaId) {
this.mediaId = mediaId;
}

public ImageMessage(Map<String, String> requestMap,String mediaId) {
super(requestMap);
this.setMsgType("image");
this.mediaId = mediaId;
}

@Override
public String toString() {
return "ImageMessage{" +
"mediaId=" + mediaId +",toUserName=" +getToUserName()
+",fromUserName="+getFromUserName()+",createTime="+getCreateTime()
+"}";
}
}
  • 在com.webchat.entity.message目录下创建继承BaseMessage的TextMessage类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
   package com.webchat.entity.message;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import java.util.Map;
//把Xstream转换的xml头标签里的.类名去掉
@XStreamAlias("xml")
public class TextMessage extends BaseMessage {
@XStreamAlias("Content")
private String content;

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public TextMessage(Map<String,String> requestMap,String content) {
super(requestMap);
//设置文本消息的MsgType为text
this.setMsgType("text");
this.content = content;
}

@Override
public String toString() {
return "TextMessage{" +
"content=" + content +",toUserName=" +getToUserName()
+",fromUserName="+getFromUserName()+",createTime="+getCreateTime()
+"}";
}
}
  • 在com.webchat.entity.message目录下创建继承BaseMessage的VideoMessage类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
 	package com.webchat.entity.message;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import java.util.Map;
@XStreamAlias("xml")
public class VideoMessage extends BaseMessage {
@XStreamAlias("MediaId")
private String mediaId;

@XStreamAlias("Title")
private String title;

@XStreamAlias("Description")
private String description;

public String getMediaId() {
return mediaId;
}

public void setMediaId(String mediaId) {
this.mediaId = mediaId;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public VideoMessage(Map<String, String> requestMap, String mediaId, String title, String description) {
super(requestMap);
this.setMsgType("video");
this.mediaId = mediaId;
this.title = title;
this.description = description;
}

@Override
public String toString() {
return "VideoMessage{" +
"mediaId=" + mediaId +",title="+getTitle()
+",description="+getDescription()+",toUserName=" +getToUserName()
+",fromUserName="+getFromUserName()+",createTime="+getCreateTime()
+"}";
}
}
  • 在com.webchat.entity.message目录下创建继承BaseMessage的VoiceMessage类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 	package com.webchat.entity.message;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import java.util.Map;
@XStreamAlias("xml")
public class VoiceMessage extends BaseMessage {
@XStreamAlias("MediaId")
private String mediaId;

public String getMediaId() {
return mediaId;
}

public void setMediaId(String mediaId) {
this.mediaId = mediaId;
}

public VoiceMessage(Map<String, String> requestMap, String mediaId) {
super(requestMap);
this.setMsgType("voice");
this.mediaId = mediaId;
}

@Override
public String toString() {
return "VoiceMessage{" +
"mediaId=" + mediaId +",toUserName=" +getToUserName()
+",fromUserName="+getFromUserName()+",createTime="+getCreateTime()
+"}";
}
}
  • 在com.webchat.entity.message目录下创建继承BaseMessage的MusicMessage类,还要建music的实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 	package com.webchat.entity.message;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import java.util.Map;
@XStreamAlias("xml")
public class MusicMessage extends BaseMessage {
@XStreamAlias("Music")
private Music music;

public Music getMusic() {
return music;
}

public void setMusic(Music music) {
this.music = music;
}

public MusicMessage(Map<String, String> requestMap, Music music) {
super(requestMap);
this.setMsgType("music");
this.music = music;
}

@Override
public String toString() {
return "MusicMessage{" +
"title=" + music.getTitle() +", description=" + music.getDescription()
+", musicURL=" + music.getMusicURL()+", hQMusicUrl=" + music.gethQMusicUrl()
+", thumbMediaId=" + music.getThumbMediaId()+",toUserName=" +getToUserName()
+",fromUserName="+getFromUserName()+",createTime="+getCreateTime()
+'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
 	package com.webchat.entity.message;
import com.thoughtworks.xstream.annotations.XStreamAlias;
@XStreamAlias("item")
public class Music {
private String title;
private String description;
private String musicURL;
private String hQMusicUrl;
private String thumbMediaId;

public Music(String title, String description, String musicURL, String hQMusicUrl, String thumbMediaId) {
this.title = title;
this.description = description;
this.musicURL = musicURL;
this.hQMusicUrl = hQMusicUrl;
this.thumbMediaId = thumbMediaId;
}

public String getThumbMediaId() {
return thumbMediaId;
}

public void setThumbMediaId(String thumbMediaId) {
this.thumbMediaId = thumbMediaId;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String getMusicURL() {
return musicURL;
}

public void setMusicURL(String musicURL) {
this.musicURL = musicURL;
}

public String gethQMusicUrl() {
return hQMusicUrl;
}

public void sethQMusicUrl(String hQMusicUrl) {
this.hQMusicUrl = hQMusicUrl;
}

@Override
public String toString() {
return "Music{" +
"title='" + title + '\'' +
", description='" + description + '\'' +
", musicURL='" + musicURL + '\'' +
", hQMusicUrl='" + hQMusicUrl + '\'' +
", thumbMediaId='" + thumbMediaId + '\'' +
'}';
}
}
  • 在com.webchat.entity.message目录下创建继承BaseMessage的NewsMessage类,还要建Article的实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
   package com.webchat.entity.message;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@XStreamAlias("xml")
public class NewsMessage extends BaseMessage {
@XStreamAlias("ArticleCount")
private String articleCount;
@XStreamAlias("Articles")
private List<Article> articles = new ArrayList<>();

public String getArticleCount() {
return articleCount;
}

public void setArticleCount(String articleCount) {
this.articleCount = articleCount;
}

public List<Article> getArticles() {
return articles;
}

public void setArticles(List<Article> articles) {
this.articles = articles;
}

public NewsMessage(Map<String, String> requestMap, List<Article> articles) {
super(requestMap);
this.setMsgType("news");
this.articleCount = articles.size()+"";
this.articles = articles;
}

@Override
public String toString() {
return "NewsMessage{" +
"articleCount='" + articleCount + '\'' +
", articles=" + articles +
'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 	package com.webchat.entity.message;
import com.thoughtworks.xstream.annotations.XStreamAlias;
@XStreamAlias("item")
public class Article {
@XStreamAlias("Title")
private String title;

@XStreamAlias("Description")
private String description;

@XStreamAlias("PicUrl")
private String picUrl;

@XStreamAlias("Url")
private String url;

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String getPicUrl() {
return picUrl;
}

public void setPicUrl(String picUrl) {
this.picUrl = picUrl;
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public Article(String title, String description, String picUrl, String url) {
this.title = title;
this.description = description;
this.picUrl = picUrl;
this.url = url;
}

@Override
public String toString() {
return "Article{" +
"title='" + title + '\'' +
", description='" + description + '\'' +
", picUrl='" + picUrl + '\'' +
", url='" + url + '\'' +
'}';
}
}