微信回调加解密 Java

背景:

微信被动回复消息,微信会调用我们自己的服务,传递一些敏感数据,为了确保安全性,可以给参数加密,当然,如果微信传的参数加密了,我们获取数据的时候自然需要解密,那么加解密需要怎么做呢?

如下图:我们在公众号基础配置里,将消息加密方式设置成「安全模式」之后,传递的就是加密之后的消息。
微信公众号平台:https://mp.weixin.qq.com/
在这里插入图片描述

在公众号开发文档里面,可以找到「消息加解密说明」并且附上了代码下载位置
公众号开发文档:
https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Message_encryption_and_decryption_instructions.html
在这里插入图片描述
代码压缩包下载下来
根据自己的开发语言选择对应代码
在这里插入图片描述
解压之后,我的开发语言是Java,然后把src里的文件复制到我们自己的项目里
在这里插入图片描述
XMLParse.java 和WxInMsgBean.java 是自己定义的工具类和实体类
在这里插入图片描述

可能需要引入信息jar包,在pom.xml里配置好即可

        <!-- hutool 工具类 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.1</version>
        </dependency>
        <!-- xml -->
        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.17</version>
        </dependency>

接收微信回调推送消息Controller

    /**
     * 接收微信推送事件
     */
    @RequestMapping(value = "/checksignature", method = RequestMethod.POST, produces = {"application/xml; charset=UTF-8"})
    @ResponseBody
    public void handleWxEvent(@RequestBody WxInMsgBean bean,
                              @RequestParam(required = false) String signature,
                              @RequestParam(required = false) String timestamp,
                              @RequestParam(required = false) String nonce,
                              @RequestParam(name = "encrypt_type", required = false) String encryptType,
                              @RequestParam(name = "msg_signature", required = false) String msgSignature,
                              HttpServletResponse response) {
        log.info("--------------------接收微信推送事件-----------------------");
        response.setCharacterEncoding("UTF-8");
        wxService.handleEvent(bean, signature, timestamp, nonce, encryptType, msgSignature, response);
    }
public interface IWxService {
 
    /**
     * 微信扫码触发事件
     *
     * @param msgBean      微信用户信息
     * @param signature    签名
     * @param timestamp    时间戳
     * @param nonce        随机串
     * @param encryptType  加密方式
     * @param msgSignature 加密签名
     * @param response     response
     */
    void handleEvent(WxInMsgBean msgBean, String signature, String timestamp, String nonce, String encryptType, String msgSignature, HttpServletResponse response);
}

@Service
@Slf4j
public class WxServiceImpl implements IWxService {

    Logger logger = LoggerFactory.getLogger(WxServiceImpl.class);

    @Value("${wx.appid:''}")
    private String appid;
    @Value("${wx.secret:''}")
    private String secret;
    @Value("${kol.domainname:''}")
    private String domainname;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handleEvent(WxInMsgBean bean, String signature, String timestamp, String nonce, String encryptType, String msgSignature, HttpServletResponse response) {
        log.info("----------- signature:{},timestamp:{},nonce:{},encryptType:{},msgSignature:{}", signature, timestamp, nonce, encryptType, msgSignature);
        WxInMsgBean wxInMsgBean = new WxInMsgBean();
        String result = "";
        WXBizMsgCrypt crypt = null;
        try {
            crypt = new WXBizMsgCrypt("公众号里配置的token", "公众号里配的EncodingAESKey", appid);
            String format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>";
            String fromXml = String.format(format, bean.getEncrypt());
            // 解密收到的消息
            result = crypt.decryptMsg(msgSignature, timestamp, nonce, fromXml);
            log.info("解密后明文: " + result);
            wxInMsgBean = XmlUtils.xmlToBean(result, WxInMsgBean.class);
     
        } catch (Exception e) {
            log.error("解密异常:", e);
        }
        try {
            // openId
            String userOpenId = wxInMsgBean.getFromUserName();
            // 微信账号
            String userName = wxInMsgBean.getToUserName();
            // 事件
            String event = wxInMsgBean.getEvent();
            // 区分消息类型
            String msgType = wxInMsgBean.getMsgType();
            // ticket凭证
            String ticket = wxInMsgBean.getTicket();
              // 普通消息
            if ("text".equals(msgType)) {
                // todo 处理文本消息
            }
            // 事件推送消息
            else if ("event".equals(msgType)) {
            	String mapToXml = handleMsgAndLog(userOpenId, userName, null);
            	// 消息加密
            	String str = crypt.encryptMsg(mapToXml, timestamp, nonce);
            	log.info("用户扫码|关注|加密后: " + str);
            	response.getWriter().print(str);
           }
        } catch (Exception e) {
            logger.error("处理微信公众号请求异常:", e);
        }
    }

	/**
     * 处理消息
     */
    private String handleMsgAndLog(String fromUserName, String toUserName, AdvWxUser wxUser) {
        String mapToXml = getReturnMsgScan(fromUserName, toUserName);
        return mapToXml;
    }

	/**
     * 消息赋值
     */
    public String getReturnMsgScan(String fromUserName, String toUserName) {
        TextMessage textMessage = new TextMessage();
        textMessage.setToUserName(fromUserName);
        textMessage.setFromUserName(toUserName);
        textMessage.setCreateTime(System.currentTimeMillis());
        textMessage.setMsgType("text");
        textMessage.setContent("欢迎回来...\n" +
                "\n" +
                "通过PC端登录网站\n" + domainname +
                "\n" +
                "\n" +
                "助您玩转投资");
        return getXmlString(textMessage);
    }



	/**
     * 拼接XML文件
     */
    public String getXmlString(TextMessage textMessage) {
        String xml = "";
        if (textMessage != null) {
            xml = "<xml>";
            xml += "<ToUserName><![CDATA[";
            xml += textMessage.getToUserName();
            xml += "]]></ToUserName>";
            xml += "<FromUserName><![CDATA[";
            xml += textMessage.getFromUserName();
            xml += "]]></FromUserName>";
            xml += "<CreateTime>";
            xml += textMessage.getCreateTime();
            xml += "</CreateTime>";
            xml += "<MsgType><![CDATA[";
            xml += textMessage.getMsgType();
            xml += "]]></MsgType>";
            xml += "<Content><![CDATA[";
            xml += textMessage.getContent();
            xml += "]]></Content>";
            xml += "</xml>";
        }
        return xml;
    }

实体类

import lombok.Data;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;

/**
 * 微信公众号信息接入参数 bean
 */
@Data
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WxInMsgBean implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 开发者微信号
     */
    private String ToUserName;

    /**
     * 发送方帐号(一个OpenID)
     */
    private String FromUserName;

    /**
     * 消息创建时间 (整型)
     */
    private Long CreateTime;

    /**
     * 消息类型:  MSG_TYPE_*
     */
    private String MsgType;

    /**
     * 消息id,64位整型
     */
    private Long MsgId;

    /**
     * 文本消息内容
     */
    private String Content;

    /**
     * 图片链接(由系统生成)
     */
    private String PicUrl;

    /**
     * 图片消息媒体id,可以调用获取临时素材接口拉取数据。
     * 语音消息媒体id,可以调用获取临时素材接口拉取数据。
     * 视频消息媒体id,可以调用获取临时素材接口拉取数据。
     */
    private String MediaId;

    /**
     * 语音格式,如amr,speex等
     */
    private String Format;

    /**
     * 语音识别结果,UTF8编码<br>
     * 需开通语音识别
     */
    private String Recognition;

    /**
     * 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
     */
    private String ThumbMediaId;

    /**
     * 地理位置消息:地理位置纬度
     */
    private String Location_X;

    /**
     * 地理位置消息:地理位置经度
     */
    private String Location_Y;

    /**
     * 地理位置消息:地图缩放大小
     */
    private String Scale;

    /**
     * 地理位置消息:地理位置信息
     */
    private String Label;

    /**
     * 链接消息:消息标题
     */
    private String Title;

    /**
     * 链接消息:消息描述
     */
    private String Description;

    /**
     * 链接消息:消息链接
     */
    private String Url;

    /**
     * 事件类型  EVENT_TYPE_*
     */
    private String Event;

    /**
     * 扫描带参数二维码事件:用户未关注时,进行关注后的事件推送:事件KEY值,qrscene_为前缀,后面为二维码的参数值
     * 扫描带参数二维码事件:用户已关注时的事件推送:事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
     * 自定义菜单事件:点击菜单拉取消息时的事件推送:事件KEY值,与自定义菜单接口中KEY值对应
     * 自定义菜单事件:点击菜单跳转链接时的事件推送:事件KEY值,设置的跳转URL
     */
    private String EventKey;

    /**
     * 扫描带参数二维码事件:二维码的ticket,可用来换取二维码图片
     */
    private String Ticket;

    /**
     * 上报地理位置事件:地理位置纬度
     */
    private String Latitude;

    /**
     * 上报地理位置事件:地理位置经度
     */
    private String Longitude;

    /**
     * 上报地理位置事件:地理位置精度
     */
    private String Precision;

    /**
     * aes 加密后的文本信息
     */
    private String Encrypt;

    /**
     * 解密后携带的访问路径
     */
    private String URL;

    /**
     * 微信公众号发送模板消息给用户的发送状态
     */
    private String Status;
}

工具类 XMLUtils

import cn.hutool.core.util.StrUtil;
import com.thoughtworks.xstream.XStream;

/**
 * xml 相关工具类
 *
 */
public class XmlUtils {
    /**
     * 将 Java Bean 转化为 XML
     *
     * @param bean {@link Object}
     * @param cls  传入对象的字节码
     * @return XML 字符串
     */
    public static <T> String beanToXml(Object bean, Class<T> cls) {
        XStream stream = new XStream();
        stream.processAnnotations(cls);
        return stream.toXML(bean);
    }

    /**
     * 将 Java Bean 转化为 XML
     *
     * @param bean {@link Object}
     * @return XML 字符串
     */
    public static String beanToXml(Object bean) {
        return beanToXml(bean, bean.getClass());
    }

    /**
     * 将 xml 转化为 Bean
     *
     * @param xml   xml
     * @param cls   bean 的类型
     * @param alias 对应 bean 的别名
     * @param <T>   泛型
     * @return T
     */
    @SuppressWarnings("unchecked")
    public static <T> T xmlToBean(String xml, Class<T> cls, String alias) {
        if (StrUtil.hasBlank(xml, alias)) {
            throw new IllegalArgumentException("xml to Bean 参数错误");
        }
        XStream stream = new XStream();
        stream.alias(alias, cls);
        return (T) stream.fromXML(xml);
    }

    /**
     * 将 xml 转化为 Bean,bean 默认别名为 xml
     *
     * @param xml xml
     * @param cls bean 的类型
     * @param <T> 泛型
     * @return T
     */
    public static <T> T xmlToBean(String xml, Class<T> cls) {
        return xmlToBean(xml, cls, "xml");
    }
}

代码里写完加解密之后,微信公众号基础配置 信息就不要随便改了,「兼容模式」和「安全模式」都可以支持此加解密。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
微信支付回调结果包括两部分:一是在 HTTP 请求中的明文参数,二是经过加密后在 HTTP 请求中的密文参数。其中,明文参数需要校验其真实性,而密文参数需要进行解密才能获取其中的明文参数。 以下是Java代码示例,演示如何对微信支付回调结果进行解密: ```java import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; public class WeChatPayCallbackDecryptor { public static String decrypt(String encryptedData, String sessionKey, String iv) throws Exception { byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData); byte[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey); byte[] ivBytes = Base64.getDecoder().decode(iv); SecretKeySpec keySpec = new SecretKeySpec(sessionKeyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(ivBytes)); byte[] decryptedBytes = cipher.doFinal(encryptedBytes); return new String(decryptedBytes); } } ``` 其中,`encryptedData` 为加密后的回调结果,`sessionKey` 为微信支付 API 返回的会话密钥,`iv` 为微信支付 API 返回的加密算法的初始向量。 调用示例: ```java String encryptedData = "xxxxx"; // 加密后的回调结果 String sessionKey = "yyyyy"; // 微信支付 API 返回的会话密钥 String iv = "zzzzz"; // 微信支付 API 返回的加密算法的初始向量 String decryptedData = WeChatPayCallbackDecryptor.decrypt(encryptedData, sessionKey, iv); System.out.println(decryptedData); ``` 解密后的结果为一个 JSON 字符串,包含了微信支付回调的各项参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值