首先写俩接口
import cn.hutool.core.util.StrUtil;
import com.xhsoft.mp.service.SubscribeService;
import com.xhsoft.mp.util.HexUtils;
import com.xhsoft.mp.util.MessageUtil;
import com.xhsoft.mp.util.MsgTypeEnum;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("xxxxx")
@Api(value = "微信公众号回调", tags = "微信公众号回调")
public class WeChatCallbackController {
/**
* 验证只接受微信后台的服务请求
* 开发者通过检验signature对请求进行校验。
* 若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功。
* 验证流程如下:
* 1、将token timestamp nonce三个参数进行字典序排序;
* 2、将三个参数字符串拼接成一个字符串进行sha1加密;
* 3、将加密后的字符串与signature对比,相同则该请求来源于微信。
*/
//微信公众号配置的Token
private static final String TOKEN = "xxxxxx";
@Resource
private SubscribeService subscribeService;
@GetMapping( "/xxxxxx/authenticate/{appKey}")
public String authenticate(@PathVariable("appKey") String appKey, @RequestParam String signature, @RequestParam String timestamp,
@RequestParam String nonce, @RequestParam String echostr) {
log.info("进入微信回调get方法,微信回调参数:signature=[{}],timestamp=[{}],nonce=[{}],echostr=[{}]", signature, timestamp, nonce, echostr);
boolean result = checkSignature(signature, timestamp, nonce);
if (result) {
return echostr;
}
return "FAILURE";
}
/**
* 微信消息响应,通过公众号-基本配置-服务器配置,填写回调地址
*
* @param request 请求信息
* @return java.lang.String
*/
@PostMapping( "/xxxxxx/authenticate/{appKey}")
public String authenticate(@PathVariable("appKey") String appKey, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
log.info("进入微信回调post方法");
return authenticateOfficial(request,appKey);
}
public boolean checkSignature(String signature, String timestamp, String nonce) {
String[] array = new String[]{ TOKEN, timestamp, nonce};
//先对这三个字符串字典排序,然后拼接
Arrays.sort(array);
StringBuilder sb = new StringBuilder();
for (String s : array) {
sb.append(s);
}
//使用SHA-1算法对拼接字符串加密
MessageDigest messageDigest;
String hexStr = null;
try {
messageDigest = MessageDigest.getInstance("SHA-1");
byte[] digest = messageDigest.digest(sb.toString().getBytes());
//将加密后的字符串转换成16进制字符串
hexStr = HexUtils.bytesToHexStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
//返回校验结果
return signature.equalsIgnoreCase(hexStr);
}
@NotNull
private String authenticateOfficial(HttpServletRequest request,String appKey) throws UnsupportedEncodingException {
//将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");
val msg = MessageUtil.parseRequest(request);
// 扫码或关注公众号
log.info("wechat callback message=[{}]", JsonUtil.toJson(msg));
//消息类型
MsgTypeEnum msgTypeEnum = MsgTypeEnum.getByType((String) msg.get("Event"));
if (null == msgTypeEnum) {
return StrUtil.EMPTY;
}
//根据不同消息类型做不同处理
try {
switch (msgTypeEnum) {
case CLICK:
String eventKey = (String) msg.get("EventKey");
log.info("菜单点击事件:{}",eventKey);
break;
case EVENT:
break;
case SUBSCRIBE:
//处理关注事件
subscribeService.doSubscribeEvent(msg, msgTypeEnum,appKey);
log.info("用户关注公众号");
break;
case UNSUBSCRIBE:
log.info("用户取消关注公众号");
break;
default:
break;
}
}catch (Exception e){
log.error("处理微信公众号请求异常:", e);
}
return StrUtil.EMPTY;
}
}
public class HexUtils {
public static String bytesToHexStr(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
}
package com.xhsoft.mp.util;
import cn.hutool.core.util.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
public class MessageUtil {
/**
* 解析微信公众号回调xml
* @param request 请求
* @return
*/
public static Map<String,Object> parseRequest(HttpServletRequest request){
InputStream inputStream = null;
Map<String,Object> map = new HashMap<>();
SAXReader reader = new SAXReader();
Document document;
try {
inputStream = request.getInputStream();
//读取输入流获取文档对象
document = reader.read(inputStream);
//根据文档对象获取根节点
Element rootElement = document.getRootElement();
//获取根所有子节点
List<Element> elements = rootElement.elements();
for (Element e:elements) {
map.put(e.getName(),e.getStringValue());
}
} catch (IOException | DocumentException e) {
log.error("处理微信公众号请求异常:", e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException ioe) {
log.error("关闭inputStream异常:", ioe);
}
}
}
return map;
}
}
package com.xhsoft.mp.util;
public enum MsgTypeEnum {
EVENT("事件", "event"),
SUBSCRIBE("用户未关注时,进行关注后的事件推送", "subscribe"),
UNSUBSCRIBE("用户取消关注时的事件推送", "unsubscribe"),
SCAN("用户已关注时的事件推送", "SCAN"),
CLICK("事件类型,CLICK", "CLICK");
private final String name;
private final String type;
MsgTypeEnum(String name, String type) {
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public static MsgTypeEnum getByType(String type) {
for (MsgTypeEnum msgType : values()) {
if (msgType.getType().equals(type)) {
return msgType;
}
}
throw new IllegalArgumentException("未知的类型: " + type);
}
}
然后你需要在微信设置里配置回调服务的接口
如果遇到xml解析报错
org.dom4j.DocumentException: Error on line 1 of document : Content is not allowed in prolog. Nested exception: Content is not allowed in prolog.
那就是你的xss没有设置它
#blade配置
blade:
#xss配置
xss:
enabled: true
skip-url:
- /xxxx/xxxx/**
之后就能拿到openid了
后面就是为了发送模板消息