微信服务器与应用程序交互-客户输入及关注、取关等事件
URL
|
微信服务器转化给我们地址,传递参数给我们,并获得参数返回,java一般试用servlet来接收,也可以springmvc的action
注意:必须是80或者443端口
|
token
|
大家加解密要用到的参数
|
EncodingAESKey
|
加*** |
消息加解密方式
|
初期选择明文,不加密
|
packagecom.xbsoft.comm.wechat.comm;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Servlet implementation class WxCommServlet
*/
@WebServlet("/WxCommServlet")
public abstract class WxCommServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger log=LoggerFactory.getLogger(WxCommServlet.class);
/**
* @see HttpServlet#HttpServlet()
*/
public WxCommServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
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();
// 通过检验 signature 对请求进行校验,若校验成功则原样返回 echostr,表示接入成功,否则接入失败
if(SignUtil.checkSignature(signature, timestamp, nonce,getToken())){
out.print(echostr);
}
out.close();
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//编码设置,和微信保持一致
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
//请求校验
String signature = request.getParameter("signature");
// 时间戮
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
//加解密类型
String encrypt_type=request.getParameter("encrypt_type");
try{
if(!SignUtil.checkSignature(signature, timestamp, nonce,getToken())){//校验不成功
return;
}
PrintWriter out = response.getWriter();
//校验成功。进行业务处理
Map<String,String> map=null;
String respXml="";//返回参数
if("aes".equals(encrypt_type)){//加密模式
//由于获取不到多机构微信id的**,这里不做加密处理。如果有机构使用了安全模式名,这里推送异常
XBUtil.exceptionDeal(log, new Exception("有机构使用了加密传递"), "微信端");
}
else{//明文模式
map= MessageUtil.parseXML(request);//生成,把request里面的xml转化成java map键值对
respXml= msgDeal(map);//调用业务逻辑层进行处理,获得反馈,没有返回""字符串
}
out.println(respXml);//返回给微信服务器
out.close();
}
catch(Exception e){
e.printStackTrace();
throw new ServletException(e);
}
}
/**
* 获得在微信公众号设置的token
* @return
*/
protected abstract String getToken();
/**
* 互动消息处理
*@param userMsgMap :用户的行为参数
* @return如果没有互动反馈,则返回空字符串,否则则是xml的交互格式
*/
protected abstract String msgDeal(Map<String,String> userMsgMap);
}
|
文本消息:
用户输入的是图片
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<!-- 消息类型:text表示文本-->
<MsgType><![CDATA[text]]></MsgType>
<!-- 文本消息内容-->
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
|
图片消息:
用户输入的是图片
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<!-- 消息类型:image表示文本-->
<MsgType><![CDATA[image]]></MsgType>
<!--图片链接-->
<PicUrl><![CDATA[this is a url]]></PicUrl>
<MsgId>1234567890123456</MsgId>
</xml>
|
地理位置消息:
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1351776360</CreateTime>
<!-- 消息类型:location表示文本-->
<MsgType>event</MsgType>
<Event>LOCATION</Event>
<Latitude>23.134521</Latitude>
<Longitude>113.358803</Longitude>
<Precision>30.000000</Precision>
</xml>
|
关注公众号消息
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event></xml>
|
取消订阅号
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[unsubscribe]]></Event></xml>
|
扫码事件
|
|
-
如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
-
如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
<EventKey><![CDATA[qrscene_123123]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>
|
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,subscribe(订阅)、unsubscribe(取消订阅)
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[SCAN]]></Event>
<EventKey><![CDATA[SCENE_VALUE]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket></xml>
|
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,SCAN
EventKey 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
Ticket 二维码的ticket,可用来换取二维码图片
|
PrintWriter out = response.getWriter();
respXml= msgDeal(map);//调用业务逻辑层进行处理,获得反馈,没有返回""字符串
out.println(respXml);//返回给微信服务器
|
示例
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content></xml>
|
说明
|
|
注意
|
文本可
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[media_id]]></MediaId>
</Image></xml>
|
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>1</ArticleCount>
<Articles>
<item>
<Title><![CDATA[title1]]></Title>
<Description><![CDATA[description1]]></Description>
<PicUrl><![CDATA[picurl]]></PicUrl>
<Url><![CDATA[url]]></Url>
</item>
</Articles></xml>
|
参数 是否必须 说明
ToUserName 是 接收方帐号(收到的OpenID)
FromUserName 是 开发者微信号
CreateTime 是 消息创建时间 (整型)
MsgType 是 消息类型,图文为news
ArticleCount 是 图文消息个数;当用户发送文本、图片、视频、图文、地理位置这五种消息时,开发者只能回复1条 图文消息;其余场景最多可回复8条图文消息
Articles 是 图文消息信息,注意,如果图文数超过限制,则将只发限制内的条数
Title 是 图文消息标题
Description 是 图文消息描述
PicUrl 是 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200
Url 是 点击图文消息跳转链接
|
多个图文,是不会显示描述信息,第一个将是大图模式,只有一个才有描述
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[voice]]></MsgType>
<Voice>
<MediaId><![CDATA[media_id]]></MediaId>
</Voice></xml>
|
参数 是否必须 说明
ToUserName 是 接收方帐号(收到的OpenID)
FromUserName 是 开发者微信号
CreateTime 是 消息创建时间戳 (整型)
MsgType 是 消息类型,语音为voice
MediaId 是 通过素材管理中的接口上传多媒体文件,得到的id
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[video]]></MsgType>
<Video>
<MediaId><![CDATA[media_id]]></MediaId>
<Title><![CDATA[title]]></Title>
<Description><![CDATA[description]]></Description>
</Video></xml>
|
参数 是否必须 说明
ToUserName 是 接收方帐号(收到的OpenID)
FromUserName 是 开发者微信号
CreateTime 是 消息创建时间 (整型)
MsgType 是 消息类型,视频为video
MediaId 是 通过素材管理中的接口上传多媒体文件,得到的id
Title 否 视频消息的标题
Description 否 视频消息的描述
|
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[music]]></MsgType>
<Music>
<Title><![CDATA[TITLE]]></Title>
<Description><![CDATA[DESCRIPTION]]></Description>
<MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>
<HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>
<ThumbMediaId><![CDATA[media_id]]></ThumbMediaId>
</Music></xml>
|
参数 是否必须 说明
ToUserName 是 接收方帐号(收到的OpenID)
FromUserName 是 开发者微信号
CreateTime 是 消息创建时间 (整型)
MsgType 是 消息类型,音乐为music
Title 否 音乐标题
Description 否 音乐描述
MusicURL 否 音乐链接
HQMusicUrl 否 高质量音乐链接,WIFI环境优先使用该链接播放音乐
ThumbMediaId 是 缩略图的媒体id,通过素材管理中的接口上传多媒体文件,得到的id
|
jdom解析
|
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom</artifactId>
<version>2.0.2</version>
</dependency>
|
实现方法
|
public static Map<String,String> parseXML(HttpServletRequest request) throws Exception{
Map<String,String> map=new HashMap<String,String> ();
SAXReader reader = new SAXReader();
Document doc=reader.read(request.getInputStream());
Element root = doc.getRootElement();
digui(root,map);
return map;
}
/**
* 递归循环函数
* @param e
* @throws Exception
*/
private static void digui(Element e,Map<String,String> map)throws Exception{
List<Element> list=e.elements();
if(list.size()==0){
System.out.println(e.getName()+"=>"+e.getTextTrim());
map.put(e.getName(), e.getTextTrim());
}
else{
for(Element ee:list){
digui(ee,map);
}
}
}
|
使用xstream
|
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.11.1</version></dependency>
|
实现方法
|
private static XStream xStream=new XStream(new MyXppDriver());//xstream解析类
/**
* 把文本消息类映射成xml
* @param textMessage
* @return
*/
public static String messageToXml(TextMessage textMessage){
xStream.alias("xml", TextMessage.class);//把把默认类转换成xml
return xStream.toXML(textMessage) ;
}
/**
* 把图像消息类映射成xml
* @param textMessage
* @return
*/
public static String messageToXml(ImageMessage imageMessage){
xStream.alias("xml", ImageMessage.class);//把把默认类转换成xml
return xStream.toXML(imageMessage) ;
}
/**
* 把语音消息类映射成xml
* @param textMessage
* @return
*/
public static String messageToXml(VoiceMessage voiceMessage){
xStream.alias("xml", VoiceMessage.class);//把把默认类转换成xml
return xStream.toXML(voiceMessage) ;
}
/**
* 把语音消息类映射成xml
* @param textMessage
* @return
*/
public static String messageToXml(MusicMessage musicMessage){
xStream.alias("xml", MusicMessage.class);//把把默认类转换成xml
return xStream.toXML(musicMessage) ;
}
/**
* 把图文消息类映射成xml
* @param textMessage
* @return
*/
public static String messageToXml(NewsMessage newsMessage){
xStream.alias("xml", NewsMessage.class);//把把默认类转换成xml
xStream.alias("item", Article.class);
return xStream.toXML(newsMessage) ;
}
/**
* 把视频消息类映射成xml
* @param textMessage
* @return
*/
public static String messageToXml(VideoMessage videoMessage){
xStream.alias("xml", VideoMessage.class);//把把默认类转换成xml
return xStream.toXML(videoMessage) ;
}
|
3.签名算法
/** * 验证签名 * @param signature * @param timestamp * @param nonce * @return */ public static boolean checkSignature(String signature, String timestamp, String nonce,String tonken){ String[] arr = new String[]{tonken, timestamp, nonce}; // 1.将 token, timestamp, nonce 三个参数进行字典排序 Arrays.sort(arr); StringBuilder content = new StringBuilder(); for(int i = 0; i < arr.length; i++){ content.append(arr[i]);//链接成字符串 } MessageDigest md = null; String tmpStr = null;
try { // 3.将三个参数字符串拼接成一个字符串进行 shal 加密 md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } content = null; // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信 return tmpStr != null ? tmpStr.equals(signature.toUpperCase()): false; }
/** * 将字节数组转换为十六进制字符串 * @param digest * @return */ private static String byteToStr(byte[] digest) { // TODO Auto-generated method stub String strDigest = ""; for(int i = 0; i < digest.length; i++){ strDigest += byteToHexStr(digest[i]); } return strDigest; }
/** * 将字节转换为十六进制字符串 * @param b * @return */ private static String byteToHexStr(byte b) { // TODO Auto-generated method stub char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; char[] tempArr = new char[2]; tempArr[0] = Digit[(b >>> 4) & 0X0F]; tempArr[1] = Digit[b & 0X0F];
String s = new String(tempArr); return s; } |
1.用户发消息自动回复
|
不依赖公众号配置文本,比如回复1,自动反馈需要去操作的应用程序功能
|
2.关注、取关用户
|
获取关注和取关信息,存储在数据库里分析
|
3.带参数二维码扫码及用户关系
|
因为现在微信禁止在功能里做强制关注公众号,所以通过带参数的公众号二维码进行公众号关注,常见场景:
1.推广海报,生成带分享用户ID的关注二维码,能记录分享者与浏览者的关系
2.功能,如投票功能,也可生成二维码,携带功能ID及用户ID参数,通过文本或者图文消息引导用户到相关功能,并能记录用户关系
|