微信回调加解密 Java

背景:

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

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

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

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

        
        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>5.6.1version>
        dependency>
        
        <dependency>
            <groupId>com.thoughtworks.xstreamgroupId>
            <artifactId>xstreamartifactId>
            <version>1.4.17version>
        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 = "";
            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 += textMessage.getToUserName();
            xml += "]]>";
            xml += ";
            xml += textMessage.getFromUserName();
            xml += "]]>";
            xml += "";
            xml += textMessage.getCreateTime();
            xml += "";
            xml += ";
            xml += textMessage.getMsgType();
            xml += "]]>";
            xml += ";
            xml += textMessage.getContent();
            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编码
* 需开通语音识别 */
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    泛型
     * @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  泛型
     * @return T
     */
    public static <T> T xmlToBean(String xml, Class<T> cls) {
        return xmlToBean(xml, cls, "xml");
    }
}

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

你可能感兴趣的:(java,消息加解密,微信回调)