微信公众号(小程序)验证URL和事件推送

发布时间:2024年01月05日

文章的内容适用小程序和公众号

微信官方示例代码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;
    }

文章来源:https://blog.csdn.net/cjymym/article/details/135413587
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。