在开发者首次提交验证申请时,微信服务器将发送GET请求到填写的URL上,并且带上四个参数(signature、timestamp、nonce、echostr),开发者通过对签名(即signature)的效验,来判断此条消息的真实性。
此后,每次开发者接收用户消息的时候,微信也都会带上前面三个参数(signature、timestamp、nonce)访问开发者设置的URL,开发者依然通过对签名的效验判断此条消息的真实性。效验方式与首次提交验证申请一致。
开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。
加密/校验流程如下:
1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
//获取微信服务器发送的校验信息
String signature = req.getParameter("signature");
String timestamp = req.getParameter("timestamp");
String nonce = req.getParameter("nonce");
String echostr = req.getParameter("echostr");
//自定义token
String token = "cpicwechat";
if( null == timestamp || null == nonce ){
return;
}
//字典排序
String[] array = { token, timestamp, nonce };
Arrays.sort(array);
String result = "";
for (String i : array) {
result += i;
}
//sha-1加密
String ret = "";
PrintWriter wt = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] hash = md.digest(result.getBytes());
Formatter formatter = new Formatter();
for (byte b : hash) {
formatter.format("%02x", b);
}
ret = formatter.toString();
//判断是否相等
if (ret.equalsIgnoreCase(signature)) {
//edit by yaoxing 20160727 begin 移动到finally
wt = resp.getWriter();
wt.print(echostr);
//wt.close();
//edit by yaoxing 20160727 end
}
} catch (NoSuchAlgorithmException ex) {
} finally {
//edit by yaoxing 20160727 begin 移动到finally
if(null != wt){
wt.close();
wt = null;
}
//edit by yaoxing 20160727 end
}
}
当校验连接成功后,用户可以向开发者服务器发送信息,该信息被微信服务器拦截并将POST消息的XML数据包到开发者填写的URL上,
不论是哪种类型的MsgType 都包含一下基本信息:
package com.cpic.wechat.pojo;
import org.xml.sax.helpers.DefaultHandler;
import com.cpic.wechat.entity.User;
public class MsgReceive extends DefaultHandler {
protected String toUserName;
protected String fromUserName;
protected String createTime;
protected String msgType;
public MsgSend process(User user) {
return null;
}
public String getToUserName() {
return toUserName;
}
public String getFromUserName() {
return fromUserName;
}
public String getCreateTime() {
return createTime;
}
public String getMsgType() {
return msgType;
}
public void setToUserName(String toUserName) {
this.toUserName = toUserName;
}
public void setFromUserName(String fromUserName) {
this.fromUserName = fromUserName;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public void setMsgType(String msgType) {
this.msgType = msgType;
}
}
开发者服务器得到该xml信息时,在doPost()方法中进行解析,响应
将每一种请求类型转换成对应的javaBean:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
Logger logger = Logger.getLogger(WeixinServlet.class);
// long i = System.currentTimeMillis();
MsgReceive msgReceive = null;
String msgType = request.getAttribute("msgType").toString();
String wxMsgXml = request.getAttribute("wxMsgXml").toString();
String returnXml = "";
PrintWriter out = response.getWriter();
logger.info("接受报文:" + wxMsgXml);
//判断msgType是哪种类型并把该类型的xml包转变成对应的JavaBean
if (msgType.equals("text")) {
msgReceive = TextReceive.toMsgBean(wxMsgXml);
} else if (msgType.equals("event")) {
msgReceive = EventReceive.toMsgBean(wxMsgXml);
} else if (msgType.equals("image")) {
msgReceive = PicReceive.toMsgBean(wxMsgXml);
} else if (msgType.equals("location")) {
msgReceive = LocationReceive.toMsgBean(wxMsgXml);
} else if (msgType.equals("link")) {
msgReceive = LinkReceive.toMsgBean(wxMsgXml);
} else if (msgType.equals("voice")) {
msgReceive = VoiceReceive.toMsgBean(wxMsgXml);
} else if (msgType.equals("video")) {
msgReceive = VideoReceive.toMsgBean(wxMsgXml);
} else {
msgReceive = UnknownReceive.toMsgBean(wxMsgXml);
}