1.接收公众号消息需要在公网,所以开发测试时需要准备内网转公网域名的插件natapp
2.下载地址https://natapp.cn/,natapp官网教程https://natapp.cn/article/natapp_newbie
3.接收公众号消息之前需要验证我们在公众号平台上填写的token, http://3axbzd.natappfree.cc是natapp公网映射的本地8080端口域名,wx/check为本地接口URL,token值随便填写,但是验证时需要与本地相同
4.查看消息接口使用指南,其中token是在页面填写的token
5.对token、timestamp、nonce三个字段进行字典排序和加密算法然后进行验证
/*
* @Author
* @Description //1)将token、timestamp、nonce三个参数进行字典序排序 2)将三个参数字符串拼接成一个字符串进行sha1加密 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
* @Date 13:34 2020/7/23
* @Param [signature, timestamp, nonce, token]
* @return {@link boolean}
**/
public boolean checkSignature(String signature, String timestamp, String nonce,String token){
try {
String shaToken=dictionarySorting(timestamp,nonce,token);
if(signature.equals(shaEncode(shaToken))){
return true;
}
} catch (Exception e) {
log.error(e.getMessage(),e);
}
return false;
}
/*
* @Author
* @Description //字典排序
* @Date 13:32 2020/7/23
* @Param [token, timestamp, nonce]
* @return {@link java.lang.String}
**/
public String dictionarySorting(String token,String timestamp,String nonce){
String[] shaToken = new String[] { token, timestamp, nonce };
Arrays.sort(shaToken);
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < shaToken.length; i++) {
stringBuilder.append(shaToken[i]);
}
return stringBuilder.toString();
}
/*
* @Author
* @Description //sha1加密算法
* @Date 13:25 2020/7/23
* @Param [inStr]
* @return {@link java.lang.String}
**/
public static String shaEncode(String inStr) throws Exception {
MessageDigest sha = null;
try {
sha = MessageDigest.getInstance("SHA-1");
} catch (Exception e) {
log.info(e.getMessage(),e);
return "";
}
byte[] byteArray = inStr.getBytes("UTF-8");
byte[] md5Bytes = sha.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
6.编写验证token的接口
@RequestMapping(value="/check")
public String wxChartInterface(HttpServletRequest request, HttpServletResponse response){
log.info("验证微信消息开始。。。");
try {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
//微信加密签名
String signature=request.getParameter("signature");
String timestamp=request.getParameter("timestamp");
String echostr = request.getParameter("echostr");
String nonce = request.getParameter("nonce");
log.info("signature={},timestamp={},echostr={},nonce={}",signature,timestamp,echostr,nonce);
if (CheckInterfaceUtil.me().checkSignature(signature, timestamp, nonce,token)){
log.info("验证微信消息结束。。。");
return echostr;
}
} catch (UnsupportedEncodingException e) {
log.error("验证微信消息异常。。。",e);
}
log.info("验证微信消息失败。。。");
return null;
}
7.点击提交,验证token
8.查看接收消息的消息文档,编写实体类,由于公众号消息全部是xml格式,所以编写实体时加入xml注解,接收的url地址为验证token消息时的url地址,但是请求方式为POST
@Data
@XStreamAlias("xml")
public class ReqMessage {
@XStreamAlias("ToUserName")
protected String ToUserName;
@XStreamAlias("FromUserName")
protected String fromUserName;
@XStreamAlias("CreateTime")
protected long createTime;
@XStreamAlias("MsgType")
protected String msgType;
@XStreamAlias("Content")
private String content;//文本消息内容
@XStreamAlias("ediaId")
private String mediaId;//语音消息媒体id,可以调用获取临时素材接口拉取数据。
@XStreamAlias("Format")
private String format;//语音格式,如amr,speex等
@XStreamAlias("MsgId")
private String msgId;//消息id,64位整型
@XStreamAlias("MediaID")
private String mediaID;//语音消息媒体id,可以调用获取临时素材接口拉取该媒体
@XStreamAlias("Recognition")
private String recognition;//语音识别结果,UTF8编码
@XStreamAlias("ThumbMediaId")
private String thumbMediaId;//视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
@XStreamAlias("Location_X")
private String location_X;//地理位置维度
@XStreamAlias("Location_Y")
private String location_Y;//Location_Y
@XStreamAlias("Scale")
private String scale;//地图缩放大小
@XStreamAlias("Title")
private String title;//消息标题
@XStreamAlias("Description")
private String description;//消息描述
@XStreamAlias("Url")
private String url;//消息链接
}
@Value("${token}")
private String token;
@RequestMapping(value="/check")
public String wxChartInterface(HttpServletRequest request, HttpServletResponse response){
log.info("验证微信消息开始。。。");
if(request.getMethod().equals("POST")){//POST请求为请求消息
weChartInterface2( request, response);
return null;
}
try {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
//微信加密签名
String signature=request.getParameter("signature");
String timestamp=request.getParameter("timestamp");
String echostr = request.getParameter("echostr");
String nonce = request.getParameter("nonce");
log.info("signature={},timestamp={},echostr={},nonce={}",signature,timestamp,echostr,nonce);
if (CheckInterfaceUtil.me().checkSignature(signature, timestamp, nonce,token)){
log.info("验证微信消息结束。。。");
return echostr;
}
} catch (UnsupportedEncodingException e) {
log.error("验证微信消息异常。。。",e);
}
log.info("验证微信消息失败。。。");
return null;
}
//@RequestMapping(value="/check",method = RequestMethod.POST)
public void weChartInterface2(HttpServletRequest request, HttpServletResponse response){
log.info("接收到公众号消息");
response.setCharacterEncoding("utf-8");
//将微信请求xml转为实体对象
ReqMessage reqMessage = MessageUtils.me().xmlToMessage(request);
log.info("公众号消息体为={}", JSON.toJSONString(reqMessage));
String message = MessageSendUtils.me().sendMessage(reqMessage);
try(PrintWriter out=response.getWriter()) {
out.write(message);
log.error("公众号消息回复成功,message={}",message);
} catch (IOException e) {
log.error("公众号消息回复失败");
}
}
9.编写方法接收并回复公众号消息
/*
* @Author
* @Description //拼写回复消息体
* @Date 15:54 2020/7/23
* @Param [reqMessage]
* @return {@link java.lang.String}
**/
public String sendMessage(ReqMessage reqMessage) {
ReqMessage resMessage = new ReqMessage();
resMessage.setToUserName(reqMessage.getFromUserName());//回复消息时交换发送人和收件人
resMessage.setFromUserName(reqMessage.getToUserName());
resMessage.setMsgType("text");//回复文本格式
resMessage.setContent("欢迎关注");
resMessage.setCreateTime(System.currentTimeMillis());
return messageToXml(resMessage);
}
public String messageToXml(ReqMessage resMessage){
XStream xstream = new XStream();
xstream.processAnnotations(resMessage.getClass());
return xstream.toXML(resMessage);
}
10.发送消息获取回复
11.使用图灵机器人接口智能回复
public String sendMessage(ReqMessage reqMessage) {
ReqMessage resMessage = new ReqMessage();
resMessage.setToUserName(reqMessage.getFromUserName());//回复消息时交换发送人和收件人
resMessage.setFromUserName(reqMessage.getToUserName());
resMessage.setMsgType("text");//回复文本格式
resMessage.setContent(getRespMessage(reqMessage.getContent()));
resMessage.setCreateTime(System.currentTimeMillis());
return messageToXml(resMessage);
}
public String messageToXml(ReqMessage resMessage){
XStream xstream = new XStream();
xstream.processAnnotations(resMessage.getClass());
return xstream.toXML(resMessage);
}
public String getRespMessage(String message){
Map map=new HashMap<>();
map.put("key","0b96821f992fade2720ef2a922ca7f6e");
map.put("question",message);
String url=OKHttp3Utils3.getUrl(map,ApiEum.ROBBOT_URL.getInfo(),"UTF-8");
CtBossSender ctBossSender=new CtBossSender();
JSONObject nutMap=ctBossSender.sendHttpGetRobbot(url);
return nutMap.getJSONArray("newslist").getJSONObject(0).getString("reply");
}
public enum ApiEum {
ACCESS_TOKEN_URL("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"),
//ROBBOT_URL("http://api.tianapi.com/txapi/robot/index"),
ROBBOT_URL("http://api.tianapi.com/txapi/tuling/index"),
BASE_URL(" https://api.weixin.qq.com/cgi-bin/"),
DELETE_PERSONAL_MENU_URL("menu/delconditional"),//删除个性化菜单
CREATE_PERSONALIZED_MENU_URL("menu/addconditional"),//创建个性化菜单
MENU_GET_URL("menu/get"),//自定义菜单的查询接口
MENU_DELETE_URL("menu/delete"),//自定义菜单删除
MENU_CREATE_URL("menu/create");//自定义菜单创建
private String info;
ApiEum(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
}
12.发送消息,获取机器人回复
ps:源码地址https://gitee.com/bjiangAnhui/yichengyoushaonian/tree/master/boot-wechart