文章的内容适用小程序和公众号
微信官方示例代码demo下载地址:微信官方demo代码 必须下载,后面要用到
把微信官方示例代码也就是下图中除了 WXBizMsgCryptTest 文件外的所有文件复制到项目中
下面是全部代码,有问题请留言
代码中只提到了关注事件作为示例,也可以处理其他事件,在于你的逻辑处理
其中的 token ,encodingAesKey,来自于微信公众号后台的消息推送服务器配置信息,详情请点击下方官方文档链接查看,这里就不叙述了
微信消息推送服务器配置官方文档说明
代码中请求路径是一样的,对应微信公众号后台配置的服务器URL
GET请求用来验证服务器的,POST请求用来处理所有事件的
Controller
import cn.hutool.core.util.StrUtil;
import com.mrc.backend.account.constant.RedisKeyConstant;
import com.mrc.willbe.saas.mall.backend.account.param.FollowEventParam;
import com.mrc.willbe.saas.mall.backend.account.service.PublicAccountService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/opr/public/account")
public class PublicAccountController {
public final static String PUSH_SUCCESS = "success";
@Autowired
private PublicAccountService publicAccountService;
@Autowired
private RedissonClient redissonClient;
/**
* 验证URL
* @param map
* @return
* @throws Exception
*/
@GetMapping("/msg")
public String serviceCheck(@RequestParam Map<String, String> map) throws Exception {
String signature = map.get("signature");
String timestamp = map.get("timestamp");
String nonce = map.get("nonce");
String echostr = map.get("echostr");
// 验证微信服务器URL
if (StrUtil.isNotBlank(signature) && StrUtil.isNotBlank(timestamp) && StrUtil.isNotBlank(nonce) && StrUtil.isNotBlank(echostr)) {
Boolean result = publicAccountService.verifyWechatServiceUrl(signature, timestamp, nonce);
if (result) {
return echostr;
} else {
log.error("验证公众号消息推送失败");
}
}else{
log.error("验证公众号消息推送失败");
}
return "";
}
/**
* 处理消息推送事件
* @param map
* @param request
* @return
* @throws Exception
*/
@PostMapping(value = "/msg")
public String handleEvent(@RequestParam Map<String, String> map,HttpServletRequest request) throws Exception {
// 验证消息并解密
FollowEventParam followEventParam = publicAccountService.verifyMsgAndDecrypt(map, request);
// 上锁 (上锁看情况,不需要就去除)
String str = followEventParam.getEvent() + followEventParam.getCreateTime();
String redissonLockKey = RedisKeyConstant.PUBLIC_ACCOUNT_MESSAGE_PUSH_LOCK + str;
RLock lock = redissonClient.getLock(redissonLockKey);
lock.lock();
try {
// 关注事件
Boolean followResult = publicAccountService.followEvent(followEventParam);
if(followResult){
return PUSH_SUCCESS;
}
log.error("本次消息推送无法处理");
return "";
} catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
} finally {
// 解锁
lock.unlock();
}
}
}
Service
import cn.hutool.core.util.StrUtil;
import cn.mrc.backend.account.aes.AesException;
import cn.mrc.backend.account.aes.WXBizMsgCrypt;
import cn.mrc.backend.account.param.FollowEventParam;
import cn.mrc.backend.account.service.PublicAccountService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Map;
@Slf4j
@Service
public class PublicAccountServiceImpl implements PublicAccountService {
String token = "你的token";
String encodingAesKey = "你的encodingAesKey ";
String appId = "你的公众号appId";
@Autowired
private ObjectMapper objectMapper;
@Override
public Boolean verifyWechatServiceUrl(String signature, String timestamp, String nonce) throws AesException {
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
return pc.verifyUrl(signature, timestamp, nonce);
}
@Override
public FollowEventParam verifyMsgAndDecrypt(Map<String, String> map, HttpServletRequest request) throws Exception {
String encryptData = formatXMLData(request);
String signature = map.get("signature");
String timestamp = map.get("timestamp");
String nonce = map.get("nonce");
String msgSignature = map.get("msg_signature");
String openid = map.get("openid");
String encryptType = map.get("encrypt_type");
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
// 验签并解密得到XML数据包
String xmlData = pc.decryptMsg(signature, timestamp, nonce, encryptData);
// XML数据包转为JSON
XmlMapper xmlMapper = new XmlMapper();
Object jsonObject = xmlMapper.readValue(xmlData, Object.class);
String jsonData = objectMapper.writeValueAsString(jsonObject);
// JSON转对象
return objectMapper.readValue(jsonData, new TypeReference<FollowEventParam>() {
});
}
@Override
public Boolean followEvent(FollowEventParam followEventParam){
if(StrUtil.isBlank(followEventParam.getMsgType()) || StrUtil.isBlank(followEventParam.getEvent())){
return false;
}
if (followEventParam.getMsgType().equals("event") && (followEventParam.getEvent().equals("subscribe"))) {
// 关注
log.info(followEventParam.getToUserName() + "::" + followEventParam.getFromUserName() + ",关注");
return true;
} else if (followEventParam.getMsgType().equals("event") && (followEventParam.getEvent().equals("unsubscribe"))) {
// 取消关注
log.info(followEventParam.getToUserName() + "::" + followEventParam.getFromUserName() + ",取消关注");
return true;
} else {
return false;
}
}
private String formatXMLData(HttpServletRequest request) {
DocumentBuilderFactory xmlFactory = DocumentBuilderFactory.newInstance();
TransformerFactory strFactory = TransformerFactory.newInstance();
try {
DocumentBuilder builder = xmlFactory.newDocumentBuilder();
InputStream inputStream = request.getInputStream();
Document document = builder.parse(inputStream);
Transformer transformer = strFactory.newTransformer();
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(writer));
return writer.getBuffer().toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}