用spring搭建微信公众号开发者模式下服务器处理用户消息的加密传输构架(java)

    要搭建加密传输的微信公众号消息传输,首先要在开发这平台下载一下微信加密的相关jar包,并做一些准备。准备的步骤如下:


1.打开开发者文档,找到消息加减密--->接入指引,如下图所示:



2.在页面底部找到实例代码,并下载解压,下载的地方如下图所示:


3.在解压的地方找到java版本,并做如下准备:

(1)将commons-codec-1.9.jar导入到项目中。



(2)将dist下的aes-jre1.6.jar导入到项目中。


(3)打开readme.txt,按照他的指示操作。红色对勾的地方很重要。


要下载的东西就是下图标记的地方


这样准备工作就完成了,可以进入正式的代码编写了。


代码部分的解释基本都放在注释中,首先开用来核心控制的controller。内容如下:

package org.calonlan.soulpower.controller;import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.Map;import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.calonlan.soulpower.service.WeiInfoService;
import org.calonlan.soulpower.util.AesUtils;
import org.calonlan.soulpower.util.MessageUtil;
import org.calonlan.soulpower.util.SignUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;/*** @author Administrator*         这个controller是用来处理消息的核心controller,在这里有两个请求地址都为/sign的请求地址,一个是用get方式,用来*         做服务器和微信服务器之间的认证;一个是post方式,用来在服务器和微信服务器之间传递消息。*/
@Controller
@RequestMapping("/core")
public class CoreController {@Resourceprivate WeiInfoService weiInfoService;/** 这个service大家可以不用管,* 因为在我的项目中我是把所有的微信相关的配置信息都保存在一个weiInfo的类中了* ,这里只会用到token。*//*** @param request* @param response* @throws IOException* @throws ServletException* 用来和微信服务器之间相互认证*/@RequestMapping(method = RequestMethod.GET, value = "/sign")public void goGet(HttpServletRequest request, HttpServletResponse response)throws IOException, ServletException {String signature = request.getParameter("signature");// 获得signatureString timestamp = request.getParameter("timestamp");// 获得timestampString nonce = request.getParameter("nonce");// 获得nonceString echostr = request.getParameter("echostr");// 获得echostr/** 上面的四个参数都是微信服务器发送过来的,其中signature、timestamp、nonce是要参与服务器的验证的,* 而echostr是在我们通过验证后返回给服务器告诉服务器我们就是要通讯 的那个远程服务器*/PrintWriter out = response.getWriter();if (SignUtil.checkSignature(signature, timestamp, nonce,weiInfoService.get())) {//在SignUtil中使用checkSignature来进行验证,代码附在后面。out.print(echostr);//验证通过后就把echostr返回给微信服务器。}out.close();out = null;}@RequestMapping(method = RequestMethod.POST, value = "/sign")/*** * 用来和微信服务器通信* * */public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");/** 进行消息分发 *//* 首先定义一个空的回复消息 */String respMessage = "";try {/* 从请求中获得xml格式的信息。并转化为map类型 */Map<String, String> requestMapSecret = MessageUtil.parseXml(request);//从request中获得获得xml,并解析,代码附在后面/* 获得解密后的消息正文 */String mingwenXML = AesUtils.descMessage(//用微信官方给的jar包中的AesUtils来对消息进行解密requestMapSecret.get("Encrypt"),/* 加密的消息体 */request.getParameter("msg_signature"),/* 请求中的消息签名 */request.getParameter("timestamp"),/* 时间戳 */request.getParameter("nonce"));/* 无序数列 *//* 将明文再次进行xml解析 */Map<String, String> requestMap = MessageUtil.parseXml(new StringReader(mingwenXML));//将明文的xml再次解析后放入map中,/** 获得用户发来的消息类型,并做相应的处理 */String messageType = requestMap.get("MsgType");System.out.println(messageType);/*处理不同格式的消息类型开始-------------------------------------------------------*/// 用户发来的是文本消息if (messageType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {System.out.println(requestMap.get("Content"));}// 用户发来的是图片消息else if (messageType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {}// 用户发来地理位置信息else if (messageType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) {}// 用户发来链接消息else if (messageType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {}// 用户发来音频消息else if (messageType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {}/** 事件推送的处理 */else if (messageType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {// 事件类型String eventType = requestMap.get("Event");// 订阅if (eventType.equals(MessageUtil.REQ_MESSAGE_TYPE_SUBSCRIBE)) {}// 取消订阅else if (eventType.equals(MessageUtil.REQ_MESSAGE_TYPE_UNSUBSCRIBE)) {}// 点击按钮事件else if (eventType.equals(MessageUtil.REQ_MESSAGE_TYPE_CLICK)) {}}/*处理不同格式的消息类型介绍-------------------------------------------------------*//*对于不同类型的消息的处理网上有很多高手已经发表了很多文章了,我自己也会总结一下,不过不是在这里---------*/// 给返回的消息加密AesUtils.aescMessage(respMessage,request.getParameter("timestamp"),request.getParameter("nonce"));} catch (Exception e) {e.printStackTrace();}/*返回消息给微信服务器,然后微信服务器把消息转发给用户···额,貌似我们聊什么,微信服务器都是可以截获的,*/PrintWriter out = response.getWriter();out.print(respMessage);out.close();}
}

SignUtil的checkSignature代码如下:

	public static boolean checkSignature(String signature, String timestamp,String nonce, WeiInfo info) {/** 微信服务器发送过来的signature是通过某些处理然后进行SHA1加密的,我们来用它发过来的信息自己生成一个signature,* 然后两者之间进行比对,一致的话我们就是伙伴,不一致就拒绝它*/String[] arr = new String[] { info.getToken(), timestamp, nonce };/* token是我们自己填写的,具体填写位置见下图;timestamp、nonce是微信服务器发送过来的,这里我们把他们都放到数组中。*/Arrays.sort(arr);//对数组中的数据进行字典排序。。。。要不然加密出来的东西是没用的StringBuilder content = new StringBuilder();for (int i = 0; i < arr.length; i++) {content.append(arr[i]);}//把字典排序好的数组读取成字符串。MessageDigest md = null;String tmpStr = null;try {/*进行sha1加密*/md = MessageDigest.getInstance("SHA-1");byte[] digest = md.digest(content.toString().getBytes());tmpStr = byteToStr(digest);//将加密后的byte转化为16进制字符串,这就是我们自己构造的signature} catch (NoSuchAlgorithmException e) {e.printStackTrace();}content = null;return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;//进行对比,相同返回true,不同返回false}

	private static String byteToStr(byte[] byteArray) {String strDigest = "";for (int i = 0; i < byteArray.length; i++) {strDigest += byteToHexStr(byteArray[i]);//分别把没一个byte位转换成一个16进制字符,代码见下面}return strDigest;}

	private static String byteToHexStr(byte mByte) {char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A','B', 'C', 'D', 'E', 'F' };char[] tempArr = new char[2];tempArr[0] = Digit[(mByte >>> 4) & 0X0F];tempArr[1] = Digit[mByte & 0X0F];String s = new String(tempArr);return s;}

AesUtils的具体代码

package org.calonlan.soulpower.util;import org.calonlan.soulpower.dao.impl.WeiInfoDaoImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class AesUtils {private static ApplicationContext applicationContext;/** 这里我直接使用的spring来读取weiInfo对象中的加解密密钥的内容,大家可以不用这样,直接把密钥放在这里就可以了,* 也可以放在properties文件中读取更方便*/public static String descMessage(String encrypt, String msgSignature,String timestamp, String nonce) throws AesException {applicationContext = new ClassPathXmlApplicationContext("config/application-context.xml");WeiInfoDaoImpl daoImpl = (WeiInfoDaoImpl) applicationContext.getBean("weiInfoDao");String token = daoImpl.get().getToken();//获得tokenString encodingAesKey = daoImpl.get().getEncodingAesKey();//获得加解密密钥String appId = daoImpl.get().getAppId();//获得appid,这个在开发者模式下就能看到,填写你自己的。String format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>";String fromXML = String.format(format, encrypt);WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);String result = pc.decryptMsg(msgSignature, timestamp, nonce, fromXML);return result;}/*加密和解密类似,就不做注释了*/public static String aescMessage(String replyMsg, String timestamp,String nonce) throws AesException {applicationContext = new ClassPathXmlApplicationContext("config/application-context.xml");WeiInfoDaoImpl daoImpl = (WeiInfoDaoImpl) applicationContext.getBean("weiInfoDao");String token = daoImpl.get().getToken();String encodingAesKey = daoImpl.get().getEncodingAesKey();String appId = daoImpl.get().getAppId();WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);String mingwen = pc.encryptMsg(replyMsg, timestamp, nonce);return mingwen;}
}

MessageUtil中的parseXml代码,有两个,一个是用来直接解析request中的xml,一个是用来解析字符串类型的xml


解析request中的xml代码

	public static Map<String, String> parseXml(HttpServletRequest request)throws Exception {Map<String, String> map = new HashMap<String, String>();InputStream inputStream = request.getInputStream();SAXReader reader = new SAXReader();//我用的是SAXReaderDocument document = reader.read(inputStream);Element root = document.getRootElement();@SuppressWarnings("unchecked")List<Element> elementList = root.elements();for (Element e : elementList) {map.put(e.getName(), e.getText());}inputStream.close();inputStream = null;return map;}


解析字符串中的xml代码

	public static Map<String, String> parseXml(StringReader readers) {Map<String, String> map = new HashMap<String, String>();SAXReader reader = new SAXReader();Document document = null;try {InputSource inputSource = new InputSource(readers);document = reader.read(inputSource);} catch (DocumentException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}Element root = document.getRootElement();@SuppressWarnings("unchecked")List<Element> elementList = root.elements();for (Element e : elementList) {map.put(e.getName(), e.getText());}return map;}


到这里,构建就完成了,可以用我们的服务器来和微信服务器进行密文的消息传递了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/569287.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring中AOP的使用

问题&#xff1a;什么是AOP&#xff1f; 答&#xff1a;AOP基本概念&#xff1a;Aspect-Oriented Programming&#xff0c;面向方面编程的简称&#xff0c;Aspect是一种新的模块化机制&#xff0c;用来描述分散在对象、类或方法中的横切关注点(crosscutting concern)&#xff…

mybatis自己学习的一些总结

以前一直在使用spring的JDBCTEMPLATE和hibernate做项目&#xff1b;两个都还不错&#xff0c;spring的jdbctemplate用起来比较麻烦&#xff0c;虽然很简单。而hibernate呢&#xff0c;用起来很好用&#xff0c;很方便&#xff0c;但是很多规矩&#xff0c;规则还有方法到现在都…

SSL的TCP通信

一切尽在代码中&#xff0c;额&#xff0c;自己测试的小例子&#xff0c;感觉很有用&#xff0c;做个记录。 服务器端&#xff1a; </pre><pre name"code" class"java">package com.mpc.test.clazz;import java.io.BufferedReader; import ja…

java反射的使用概览

额&#xff0c;研究过这个的人很多很多&#xff0c;但是我没有很深入的研究过&#xff0c;所以谁也拦不住我去研究研究&#xff0c;然后记录下来如有雷同那就雷同了请多多包涵。 首先是整个项目的结构&#xff1a; 使用到的类&#xff1a; package reflect.demo;public class D…

moodle3.7中文语言包

Moodle官方有中文语言包&#xff0c;但是还有没有翻译的&#xff0c;为了提高用户体验&#xff0c;可以将部分未翻译的应用在Moodle网站管理中自己修改。 具体步骤&#xff1a; 先确定需要修改的关键字&#xff0c;也就是网站中没有翻译成中文的文字在centos中定位到moodle网站…

Spring项目中使用webservice实现h5的websocket通信

一、在项目中建立一个webservice来做后台操作。 package org.calonlan.soulpower.websocket;import java.text.SimpleDateFormat; import java.util.Date;import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.we…

[连载型] Neutron 系列 (15): OpenStack 是如何实现 Neutron 网络 和 Nova虚机 防火墙的...

问题导读&#xff1a;1.Nova安全组是什么&#xff1f;2.Nova的是如何配置的?3.FWaas是什么&#xff1f;1. Nova 安全组1.1 配置 节点配置文件配置项说明controller/etc/nova/nova.confsecurity_group_api nova是的 nova secgroup* 命令使用的是 nova 安全组的 API/etc/neutro…

LeetCode题解

题目是这样的&#xff1a;一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为“Start” &#xff09;。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为“Finish”&#xff09;。问总共有多少条不同的路径&a…

java获得指定的开始时间与结束时间之间的所有日期

import java.text.SimpleDateFormat; import java.util.Calendar;public class TimerTest {public static void main(String[] args) throws Exception {String beginDate "2016-07-16";//开始时间String endDate "2016-07-25";//结束时间SimpleDateForm…

linux中umask的使用

在linux创建文件、文件夹的时候会给它们赋予默认的权限&#xff0c;这个默认权限的赋予就是和umask相关的。总结如下&#xff1a; 1&#xff1a;x 执行 2&#xff1a;w 写入 4&#xff1a;r 读取 文件创建的时候的权限为 666与umask的每一位对应相减&#xff1b;如 umask 为…

spring boot 与redis 整合

创建项目时需要导入的包 在application.yml 配置文件中配置需要的 spring:datasource:url: jdbc:mysql://localhost:3306/数据库名?useSSLfalse&serverTimezoneAsia/Shanghaiusername: 用户名password: 密码jpa:show-sql: truehibernate:ddl-auto: none #redis 可以不配,默…

Http长连接的例子_亲测可用哦

一、什么事Http长连接&#xff1a;在网上有很多很多关于Http长连接的文章&#xff0c;但是我看了很多都看不懂。自己总结的所谓的http长连接就是在一请求一个页面后&#xff0c;在服务器端不断开http连接&#xff0c;而是通过response一直在定时的往页面客户端刷新数据。 二、s…

不同操作系统上DNS客户端操作区别汇总

结论&#xff1a;windows有DNS缓存&#xff0c;Linux默认无DNS缓存&#xff0c;只能依赖于安装其他软件。 一、不同操作系统的客户端的DNS缓存差别 1、windows 系统中dns 解析器会使用系统的dns缓存来提高dns域名解析效率。 例如&#xff1a; 查看当前的dns cache内容&#xff…

SLAM学习心得——建图

1.建图 我们所谓的地图&#xff0c;即所有路标点的集合。一旦我们确定了路标点的位置&#xff0c;那就可以说我们完成了建图。 地图的作用&#xff1a;&#xff08;1&#xff09;定位 &#xff1b;&#xff08;2&#xff09;导航&#xff1b; &#xff08;3&#xff09;避障&am…

spark2

特点 通用 批处理 迭代式计算 交互查询 流处理 组件 spark core:任务调度 内存管理 容错机制 内部定义了RDDs 提供了很多API &#xff0c;为其他组件提供底层的服务 spark sql&#xff1a;报表统计 streaming :从kafka接收数据做实时统计 mlib&#xff1a;mll 支持横向扩展&am…

spark 监控--WebUi、Metrics System(转载)

转载自&#xff1a;https://www.cnblogs.com/barrenlake/p/4364644.html Spark 监控相关的部分有WebUi 及 Metrics System; WebUi用于展示Spark 资源状态、Metrics System 整合的指标信息。 Ui相关流程 Spark集群启动之后&#xff0c;我们可以通过Web观察集群状态等信息&#x…

Hadoop64位版本安装后遇到的警告处理

在使用hadoop的过程中&#xff0c;会遇到一个警告&#xff0c;内容如下&#xff1a; WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable 对于这个问题网上很多说法是由于系统位数和所下载…

ueditor跨域上传图片文件(基于jsp框架、tomcat)

额&#xff0c;好久没有用到ueditor了&#xff0c;因为现在的相关工作很少涉及到富文本编辑&#xff1b;最近临时带了一个项目&#xff0c;里面要用到富文本编辑器&#xff0c;而且文件要统一上传到文件服务器上保存&#xff1b;应为以前用过ueditor就试着在网上着一些跨域保存…

精选Pycharm里6大神器插件

http://www.sohu.com/a/306693644_752099 上次写了一篇关于Sublime的精品插件推荐&#xff0c;有小伙伴提议再来一篇Pycharm的主题。相比Sublime&#xff0c;Pycharm要强大许多&#xff0c;而且是专为python设计的集成开发环境&#xff0c;所以无论是自身功能、环境配置还是使用…

数字信号处理实验(一)——DTFT

一、离散序列傅里叶变化——DTFT 1、DTFT公式 2、Matlab算法实现 function[X]dtft(x,n,w,flag)%计算离散时间付里叶变换 %[X]dtft(x,n,w) %X在w频率点上的DTFT数组 %xn点有限长度序列 %n样本位置向量 %w频率点位置向量X x * (exp(-j).^(n * w));3、DTFT一些画图代码 function …