目录
3-1:代码解析公众号请求信息,将信息内容都放在request里,所以需要对request进行解析
一:微信端配置
在设置与开发——>基本配置中,设置服务器配置,token可以随便输入,填写之后,记录下来,马上代码中需要用到。key点击随机生成
如果此时点击提交,就会报错,token验证失败,因为上面很清楚说明了“此信息需要你拥有自己的服务器资源”,也就是说这个接口将会掉用我们自己服务上的接口,进行验证,所以准备JAVA项目
二:准备接受服务器信息,进行地址验签
/**
* 微信服务器发送信息,进行验签
* @param request
* @param response
* @return
*/
@GetMapping("/checkToken")
public String checkToken2(HttpServletRequest request, HttpServletResponse response) {
try {
if (StringUtils.isNotBlank(request.getParameter("signature"))) {
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
log.info("signature[{}], timestamp[{}], nonce[{}], echostr[{}]", signature, timestamp, nonce, echostr);
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
log.info("数据源为微信后台,将echostr[{}]返回!", echostr);
return echostr;
}
}
} catch (Exception e) {
log.error(e.getMessage());
}
return "";
}
该方法必须是get请求, 且每次修改配置点击提交之前都会进行验签,验签成功直接返回echostr,启动服务再次点击提交,服务器配置就可以保存了。
三:公众号关注、取消、输入文本等指令信息
公众号的关注和取消请求的地址都是和上面的地址相同,只不过上面的是get请求,所有的动作指令都是POST请求,请注意!
/**
* 关注公众号验签
*/
@PostMapping("/checkToken")
public void checkTokenPost(HttpServletRequest request, HttpServletResponse response) {
try {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
Map<String, String> requestMap = WxMessageUtil.parseXml(request);
log.info("解析====>{}", request);
String messageType = requestMap.get("MsgType");
log.info("微信类型===>{}", messageType);
String eventType = requestMap.get("Event");
// 发送方帐号(open_id)
String openid = requestMap.get("FromUserName");
// 公众帐号
String toUserName = requestMap.get("ToUserName");
// 消息类型
String msgType = requestMap.get("MsgType");
if (messageType.equals(WxMessageType.EVENT.getCode())) {
//判断消息类型是否是事件消息类型
log.info("公众号====>事件消息");
log.info("openid:" + openid);
log.info("Event:" + eventType);
if (eventType.equals(WxEeventType.SUBSCRIBE.getCode())) {
log.info("公众号====>新用户关注");
// 获取接口调用凭证
String accessTokenStr = "https://api.weixin.qq.com/cgi-bin/token?" +
"grant_type=client_credential" + "&appid=" + "wxXXXXXXXX" + "&secret=" + "f97XXXXXXXX";
String tokenStr = HttpUtil.get(accessTokenStr);
JSONObject object = JSONUtil.parseObj(tokenStr);
String accessToken = object.get("access_token").toString();
// 构建获取用户的基本信息
String buffer = "https://api.weixin.qq.com/cgi-bin/user/info?" +
"access_token=" + accessToken +
"&openid=" + openid + "&lang=zh_CN";
String wxUserString = HttpUtil.get(buffer);
log.info("获取用户信息===>{}", wxUserString);
JSONObject jsonObject = JSONUtil.parseObj(wxUserString);
} else if (eventType.equals(WxEeventType.UNSUBSCRIBE.getCode())) {
log.info("公众号====>用户取消关注");
} else {
log.info("微信类型===>{}", messageType);
log.info("公众号===>其他");
}
} else if (messageType.equals(WxMessageType.TEXT.getCode())) {
log.info("用户输入文本信息");
// 响应消息
PrintWriter out = response.getWriter();
TextMessage textMessage = new TextMessage();
textMessage.setFromUserName(openid);
textMessage.setToUserName(toUserName);
textMessage.setMsgType(msgType);
textMessage.setCreateTime(System.currentTimeMillis());
textMessage.setContent("欢迎您");
String message = WxMessageUtil.textMessageToXml(textMessage);
log.info("message==>{}",message);
out.println(message);
// 关闭流
out.close();
}
} catch (Exception e) {
log.error(e.getMessage());
}
}
3-1:代码解析公众号请求信息,将信息内容都放在request里,所以需要对request进行解析
3-1-1:引用Maven,编写WxMessageUtil工具类
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream -->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.19</version>
</dependency>
package com.dx.common.utils;
import com.dx.common.domain.TextMessage;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* WxMessageUtil
*
* @Description: 解析微信关注后的xml信息
* @Author: bb
* @Date: 2022-08-18
* @Version: V1.0.0
*/
public class WxMessageUtil {
/**
* 解析微信发来的请求(XML)
*
* @param request
* @return
* @throws Exception
*/
public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
// 将解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>();
// 从request中取得输入流
InputStream inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
@SuppressWarnings("unchecked")
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList) {
map.put(e.getName(), e.getText());
}
// 释放资源
inputStream.close();
inputStream = null;
return map;
}
/**
* 文本消息对象转换成xml
*
* @param textMessage 文本消息对象
* @return xml
*/
public static String textMessageToXml(TextMessage textMessage) {
xstream.alias("xml", textMessage.getClass());
return xstream.toXML(textMessage);
}
/**
* 扩展xstream,使其支持CDATA块
*
*/
private static XStream xstream = new XStream(new XppDriver() {
@Override
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
// 对所有xml节点的转换都增加CDATA标记
boolean cdata = true;
@Override
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
writer.write("<![CDATA[");
writer.write(text);
writer.write("]]>");
} else {
writer.write(text);
}
}
};
}
});
}
3-1-2:判断用户操作指令,是动作事情节点还是文本信息
package com.dx.common.enums;
import lombok.Getter;
@Getter
public enum WxMessageType {
/**
* 事件类型,比如订阅与取消订阅
*/
EVENT("event"),
/**
* 向公众号发送的文字消息
*/
TEXT("text");
private String code;
WxMessageType(String code) {
this.code = code;
}
}
package com.dx.common.enums;
import lombok.Getter;
@Getter
public enum WxEeventType {
/**
* 关注公众号
*/
SUBSCRIBE("subscribe"),
/**
* 取消关注公众号
*/
UNSUBSCRIBE("unsubscribe");
private String code;
WxEeventType(String code) {
this.code = code;
}
}
3-1-3:如果是关注指定,我们就去获取用户信息,获取用户的基本信息,又需要access_token参数和openId参数,上面已经获取到openId参数,
// 发送方帐号(open_id) String openid = requestMap.get("FromUserName");
所以我们只需要准备 access_token,这里我就简单获取一下参数,实际业务中需要定时去获取一下然后放在缓存里比较好,这样可以有效的保证access_token
// 获取接口调用凭证
String accessTokenStr = "https://api.weixin.qq.com/cgi-bin/token?" +
"grant_type=client_credential" + "&appid=" + "wxXXXX" + "&secret=" + "f977af825f980dc13XXXXXXXXXXX";
String tokenStr = HttpUtil.get(accessTokenStr);
JSONObject object = JSONUtil.parseObj(tokenStr);
String accessToken = object.get("access_token").toString();
1、需要参数appid和secret,同时设置IP白名单,即可调用获取
2、HttpUtil使用的是hutool
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.9</version>
</dependency>
3、构建获取用户的基本信息
// 构建获取用户的基本信息
String buffer = "https://api.weixin.qq.com/cgi-bin/user/info?" +
"access_token=" + accessToken +
"&openid=" + openid + "&lang=zh_CN";
String wxUserString = HttpUtil.get(buffer);
log.info("获取用户信息===>{}", wxUserString);
JSONObject jsonObject = JSONUtil.parseObj(wxUserString);
JSONObject就是用户的基本信息了包括昵称,头像,地址等
3-2:用户如果发送文本信息则进行回复
else if (messageType.equals(WxMessageType.TEXT.getCode())) {
log.info("用户输入文本信息");
// 响应消息
PrintWriter out = response.getWriter();
TextMessage textMessage = new TextMessage();
textMessage.setFromUserName(openid);
textMessage.setToUserName(toUserName);
textMessage.setMsgType(msgType);
textMessage.setCreateTime(System.currentTimeMillis());
textMessage.setContent("欢迎您");
String message = WxMessageUtil.textMessageToXml(textMessage);
log.info("message==>{}",message);
out.println(message);
// 关闭流
out.close();
}
3-2-1:准备TextMessage实体类,注意实体类首字母大写
package com.dx.common.domain;
import lombok.Data;
/**
* TextMessage
*
* @Description:
* @Author: bb
* @Date: 2022-08-19
* @Version: V1.0.0
*/
@Data
public class TextMessage {
/**
* 开发者微信号
*/
private String ToUserName;
/**
* 发送方账号
*/
private String FromUserName;
/**
* 创建时间
*/
private Long CreateTime;
/**
* 消息类型
*/
private String MsgType;
/**
* 消息文本
*/
private String Content;
}
3-2-2:根据用户输入的内容进行自定义回复