From 34e5b637383bd7b01ad39d83102e914721f87b6f Mon Sep 17 00:00:00 2001 From: ck <851316342@qq.com> Date: Fri, 7 Aug 2020 01:03:07 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8E=B7=E5=8F=96ticket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 22 +- .../com/bsd/say/config/RedisProperies.java | 124 ++++++++ .../bsd/say/controller/WechatController.java | 294 ++++-------------- .../bsd/say/service/WxOpenServiceDemo.java | 69 ++++ 4 files changed, 278 insertions(+), 231 deletions(-) create mode 100644 src/main/java/com/bsd/say/config/RedisProperies.java create mode 100644 src/main/java/com/bsd/say/service/WxOpenServiceDemo.java diff --git a/pom.xml b/pom.xml index 19050fc..3612ec3 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ 1.8 + 3.8.0 @@ -216,8 +217,27 @@ 1.4.8 + + com.github.binarywang + weixin-java-open + ${weixin-java-open.version} + - + + redis.clients + jedis + 2.9.0 + + + org.springframework.boot + spring-boot-configuration-processor + true + + + com.thoughtworks.xstream + xstream + 1.4.10 + diff --git a/src/main/java/com/bsd/say/config/RedisProperies.java b/src/main/java/com/bsd/say/config/RedisProperies.java new file mode 100644 index 0000000..9238d5b --- /dev/null +++ b/src/main/java/com/bsd/say/config/RedisProperies.java @@ -0,0 +1,124 @@ +package com.bsd.say.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import redis.clients.jedis.JedisPoolConfig; +import redis.clients.jedis.Protocol; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocketFactory; + +/** + * @author 007 + */ +@ConfigurationProperties(prefix = "wechat.redis") +public class RedisProperies extends JedisPoolConfig { + private String host = "111.229.204.101"; + private int port = 63790; + private String password = "1qaz2wsx"; + private int database = 4; + private int connectionTimeout = Protocol.DEFAULT_TIMEOUT; + private int soTimeout = Protocol.DEFAULT_TIMEOUT; + private String clientName; + private boolean ssl; + private SSLSocketFactory sslSocketFactory; + private SSLParameters sslParameters; + private HostnameVerifier hostnameVerifier; + + public boolean isSsl() { + return ssl; + } + + public void setSsl(boolean ssl) { + this.ssl = ssl; + } + + public SSLSocketFactory getSslSocketFactory() { + return sslSocketFactory; + } + + public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) { + this.sslSocketFactory = sslSocketFactory; + } + + public SSLParameters getSslParameters() { + return sslParameters; + } + + public void setSslParameters(SSLParameters sslParameters) { + this.sslParameters = sslParameters; + } + + public HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } + + public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + if (host == null || "".equals(host)) { + host = Protocol.DEFAULT_HOST; + } + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + if ("".equals(password)) { + password = null; + } + this.password = password; + } + + public int getDatabase() { + return database; + } + + public void setDatabase(int database) { + this.database = database; + } + + public String getClientName() { + return clientName; + } + + public void setClientName(String clientName) { + if ("".equals(clientName)) { + clientName = null; + } + this.clientName = clientName; + } + + public int getConnectionTimeout() { + return connectionTimeout; + } + + public void setConnectionTimeout(int connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + public int getSoTimeout() { + return soTimeout; + } + + public void setSoTimeout(int soTimeout) { + this.soTimeout = soTimeout; + } +} diff --git a/src/main/java/com/bsd/say/controller/WechatController.java b/src/main/java/com/bsd/say/controller/WechatController.java index 78a93bb..ae34c25 100644 --- a/src/main/java/com/bsd/say/controller/WechatController.java +++ b/src/main/java/com/bsd/say/controller/WechatController.java @@ -1,18 +1,28 @@ package com.bsd.say.controller; +import com.bsd.say.config.RedisProperies; +import com.bsd.say.service.WxOpenServiceDemo; +import com.bsd.say.service.impl.WeixinService; import com.bsd.say.util.LogUtils; -import com.bsd.say.util.MessageUtil; import com.bsd.say.util.Xml2MapUtil; import com.bsd.say.util.wechat.AesException; import com.bsd.say.util.wechat.WXBizMsgCrypt; +import com.sun.org.apache.bcel.internal.generic.NEW; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage; import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; import org.dom4j.DocumentException; import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; + import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -39,94 +49,43 @@ public class WechatController { private String componentAppId; @Resource private RedisTemplate redisTemplate; + @Autowired + private WxOpenServiceDemo wxOpenService; Logger logger = LogUtils.getBussinessLogger(); - + @Autowired + private WeixinService weixinService; /** * 接收component_verify_ticket 或 authorized事件 */ - @PostMapping(value = "/getComponentVerifyTicket") - @ResponseBody - public void getComponentVerifyTicket(HttpServletRequest request, HttpServletResponse response) throws Exception { - - String nonce = request.getParameter("nonce"); - String timestamp = request.getParameter("timestamp"); - String signature = request.getParameter("signature"); - String msgSignature = request.getParameter("msg_signature"); -// String postData = request.getParameter("postData"); - logger.info("nonce: " + nonce); - logger.info("timestamp: " + timestamp); - logger.info("signature: " + signature); - logger.info("msgSignature: " + msgSignature); - - - - -// Map map= MessageUtil.parseXml(request); - -// System.out.println(map.toString()); - - - - - - - - - - - - - - - - - StringBuilder sb = new StringBuilder(); - BufferedReader in = request.getReader(); - String line; - while ((line = in.readLine()) != null) { - sb.append(line); + @RequestMapping("/getComponentVerifyTicket") + public Object receiveTicket(@RequestBody(required = false) String requestBody, @RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce, @RequestParam("signature") String signature, + @RequestParam(name = "encrypt_type", required = false) String encType, + @RequestParam(name = "msg_signature", required = false) String msgSignature) { + this.logger.info( + "\n接收微信请求:[signature=[{}], encType=[{}], msgSignature=[{}]," + + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", + signature, encType, msgSignature, timestamp, nonce, requestBody); + + if (!StringUtils.equalsIgnoreCase("aes", encType) + || !wxOpenService.getWxOpenComponentService().checkSignature(timestamp, nonce, signature)) { + throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } - String postData = sb.toString(); - logger.info("postData: " + postData); - try { - //这个类是微信官网提供的解密类,需要用到消息校验Token 消息加密Key和服务平台appid - WXBizMsgCrypt pc = new WXBizMsgCrypt(componentToken, - aesKey, componentAppId); - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); - dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); - dbf.setXIncludeAware(false); - dbf.setExpandEntityReferences(false); - DocumentBuilder db = dbf.newDocumentBuilder(); - StringReader sr = new StringReader(postData); - InputSource is = new InputSource(sr); - org.w3c.dom.Document document = db.parse(is); - - org.w3c.dom.Element root = document.getDocumentElement(); - NodeList nodelist1 = root.getElementsByTagName("Encrypt"); - String encrypt = nodelist1.item(0).getTextContent(); - String format = ""; - String fromXML = String.format(format, encrypt); - String xml = pc.decryptMsg(msgSignature, timestamp, nonce, fromXML); - Map result = Xml2MapUtil.xml2map(xml);// 将xml转为map - - String componentVerifyTicket = MapUtils.getString(result, "ComponentVerifyTicket"); + // aes加密的消息 + WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(requestBody, + wxOpenService.getWxOpenConfigStorage(), timestamp, nonce, msgSignature); + this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString()); + try { + String out = wxOpenService.getWxOpenComponentService().route(inMessage); + this.logger.debug("\n组装回复信息:{}", out); + } catch (WxErrorException e) { + this.logger.error("receive_ticket", e); + } - logger.info("----verify ticket--" + componentVerifyTicket); - // 存储平台授权票据,保存ticket - String TICKET = componentVerifyTicket; - redisTemplate.opsForValue().set("component_verify_ticket", TICKET); - } catch (Exception e) { -// log.error(e.getMessage(), e); - e.printStackTrace(); - } -// WeChatUtils.responseReplyMessage(response, "success"); - output(response, "success"); + return "success"; } /** @@ -146,43 +105,7 @@ public class WechatController { } } - // /** -// * 处理授权事件的推送 -// * -// * @param request -// * @throws IOException -// * @throws AesException -// * @throws DocumentException -// */ -// public void processAuthorizeEvent(HttpServletRequest request) throws IOException, DocumentException, AesException { -// String nonce = request.getParameter("nonce"); -// String timestamp = request.getParameter("timestamp"); -// String signature = request.getParameter("signature"); -// String msgSignature = request.getParameter("msg_signature"); -// -// if (!StringUtils.isNotBlank(msgSignature)) -// return;// 微信推送给第三方开放平台的消息一定是加过密的,无消息加密无法解密消息 -// boolean isValid = checkSignature(COMPONENT_TOKEN, signature, timestamp, nonce); -// if (isValid) { -// StringBuilder sb = new StringBuilder(); -// BufferedReader in = request.getReader(); -// String line; -// while ((line = in.readLine()) != null) { -// sb.append(line); -// } -// String xml = sb.toString(); -//// LogUtil.info("第三方平台全网发布-----------------------原始 Xml="+xml); -// String encodingAesKey = COMPONENT_ENCODINGAESKEY;// 第三方平台组件加密密钥 -// String appId = getAuthorizerAppidFromXml(xml);// 此时加密的xml数据中ToUserName是非加密的,解析xml获取即可 -// //LogUtil.info("第三方平台全网发布-------------appid----------getAuthorizerAppidFromXml(xml)-----------appId="+appId); -// WXBizMsgCrypt pc = new WXBizMsgCrypt(COMPONENT_TOKEN, encodingAesKey, COMPONENT_APPID); -// xml = pc.decryptMsg(msgSignature, timestamp, nonce, xml); -//// LogUtil.info("第三方平台全网发布-----------------------解密后 Xml="+xml); -// processAuthorizationEvent(xml); -// } -// } -// -// + @RequestMapping(value = "/{appid}/callback", method = {RequestMethod.GET, RequestMethod.POST}) public void callBackEvent(HttpServletRequest request, HttpServletResponse response) throws IOException, DocumentException, AesException { @@ -209,117 +132,28 @@ public class WechatController { //// LogUtil.info("全网发布接入检测消息反馈开始---------------APPID="+ APPID +"------------------------toUserName="+toUserName); // checkWeixinAllNetworkCheck(request,response,xml); } -// -// -// public void checkWeixinAllNetworkCheck(HttpServletRequest request, HttpServletResponse response,String xml) throws DocumentException, IOException, AesException{ -// String nonce = request.getParameter("nonce"); -// String timestamp = request.getParameter("timestamp"); -// String msgSignature = request.getParameter("msg_signature"); -// -// WXBizMsgCrypt pc = new WXBizMsgCrypt(componentToken, aesKey, componentAppId); -// xml = pc.decryptMsg(msgSignature, timestamp, nonce, xml); -// -// Document doc = DocumentHelper.parseText(xml); -// Element rootElt = doc.getRootElement(); -// String msgType = rootElt.elementText("MsgType"); -// String toUserName = rootElt.elementText("ToUserName"); -// String fromUserName = rootElt.elementText("FromUserName"); -// -//// LogUtil.info("---全网发布接入检测--step.1-----------msgType="+msgType+"-----------------toUserName="+toUserName+"-----------------fromUserName="+fromUserName); -//// LogUtil.info("---全网发布接入检测--step.2-----------xml="+xml); -// if("event".equals(msgType)){ -//// LogUtil.info("---全网发布接入检测--step.3-----------事件消息--------"); -// String event = rootElt.elementText("Event"); -// replyEventMessage(request,response,event,toUserName,fromUserName); -// }else if("text".equals(msgType)){ -//// LogUtil.info("---全网发布接入检测--step.3-----------文本消息--------"); -// String content = rootElt.elementText("Content"); -// processTextMessage(request,response,content,toUserName,fromUserName); -// } -// } -// -// public void replyEventMessage(HttpServletRequest request, HttpServletResponse response, String event, String toUserName, String fromUserName) throws DocumentException, IOException { -// String content = event + "from_callback"; -//// LogUtil.info("---全网发布接入检测------step.4-------事件回复消息 content="+content + " toUserName="+toUserName+" fromUserName="+fromUserName); -// replyTextMessage(request,response,content,toUserName,fromUserName); -// } -// -// -// /** -// * 回复微信服务器"文本消息" -// * @param request -// * @param response -// * @param content -// * @param toUserName -// * @param fromUserName -// * @throws DocumentException -// * @throws IOException -// */ -// public void replyTextMessage(HttpServletRequest request, HttpServletResponse response, String content, String toUserName, String fromUserName) throws DocumentException, IOException { -// Long createTime = Calendar.getInstance().getTimeInMillis() / 1000; -// StringBuffer sb = new StringBuffer(); -// sb.append(""); -// sb.append(""); -// sb.append(""); -// sb.append(""+createTime+""); -// sb.append(""); -// sb.append(""); -// sb.append(""); -// String replyMsg = sb.toString(); -// -// String returnvaleue = ""; -// try { -// WXBizMsgCrypt pc = new WXBizMsgCrypt(componentToken, aesKey, componentAppId); -// returnvaleue = pc.encryptMsg(replyMsg, createTime.toString(), "easemob"); -//// logger.info("------------------加密后的返回内容 returnvaleue: "+returnvaleue); -// } catch (AesException e) { -// e.printStackTrace(); -// } -// output(response, returnvaleue); -// } -// -// public void processTextMessage(HttpServletRequest request, HttpServletResponse response,String content,String toUserName, String fromUserName) throws IOException, DocumentException{ -// if("TESTCOMPONENT_MSG_TYPE_TEXT".equals(content)){ -// String returnContent = content+"_callback"; -// replyTextMessage(request,response,returnContent,toUserName,fromUserName); -// }else if(StringUtils.startsWithIgnoreCase(content, "QUERY_AUTH_CODE")){ -// output(response, ""); -// //接下来客服API再回复一次消息 -// replyApiTextMessage(request,response,content.split(":")[1],fromUserName); -// } -// } -// -// public void replyApiTextMessage(HttpServletRequest request, HttpServletResponse response, String auth_code, String fromUserName) throws DocumentException, IOException { -// String authorization_code = auth_code; -// // 得到微信授权成功的消息后,应该立刻进行处理!!相关信息只会在首次授权的时候推送过来 -// logger.info("------step.1----使用客服消息接口回复粉丝----逻辑开始-------------------------"); -// try { -// ApiComponentToken apiComponentToken = new ApiComponentToken(); -// apiComponentToken.setComponent_appid(COMPONENT_APPID); -// apiComponentToken.setComponent_appsecret(COMPONENT_APPSECRET); -// WeixinOpenAccountEntity entity = getWeixinOpenAccount(APPID); -// apiComponentToken.setComponent_verify_ticket(entity.getTicket()); -// String component_access_token = JwThirdAPI.getAccessToken(apiComponentToken); -// -// logger.info("------step.2----使用客服消息接口回复粉丝------- component_access_token = "+component_access_token + "---------authorization_code = "+authorization_code); -// net.sf.json.JSONObject authorizationInfoJson = JwThirdAPI.getApiQueryAuthInfo(COMPONENT_APPID, authorization_code, component_access_token); -// logger.info("------step.3----使用客服消息接口回复粉丝-------------- 获取authorizationInfoJson = "+authorizationInfoJson); -// net.sf.json.JSONObject infoJson = authorizationInfoJson.getJSONObject("authorization_info"); -// String authorizer_access_token = infoJson.getString("authorizer_access_token"); -// -// -// Map obj = new HashMap(); -// Map msgMap = new HashMap(); -// String msg = auth_code + "_from_api"; -// msgMap.put("content", msg); -// -// obj.put("touser", fromUserName); -// obj.put("msgtype", "text"); -// obj.put("text", msgMap); -// JwThirdAPI.sendMessage(obj, authorizer_access_token); -// } catch (WexinReqException e) { -// e.printStackTrace(); -// } -// -// } + + @RequestMapping("test") + public void test(){ + JedisPool pool = new JedisPool(); + RedisProperies redisProperies = new RedisProperies(); + if (pool == null) { + synchronized (WxOpenServiceDemo.class) { + if (pool == null) { + pool = new JedisPool(redisProperies, redisProperies.getHost(), + redisProperies.getPort(), redisProperies.getConnectionTimeout(), + redisProperies.getSoTimeout(), redisProperies.getPassword(), + redisProperies.getDatabase(), redisProperies.getClientName(), + redisProperies.isSsl(), redisProperies.getSslSocketFactory(), + redisProperies.getSslParameters(), redisProperies.getHostnameVerifier()); + } + } + } + Jedis jedis = null; + RedisProperies properies = new RedisProperies(); + + jedis = pool.getResource(); + System.out.println(jedis.get("wechat_component_verify_ticket:wx474350bcaea2d745")); + } + } diff --git a/src/main/java/com/bsd/say/service/WxOpenServiceDemo.java b/src/main/java/com/bsd/say/service/WxOpenServiceDemo.java new file mode 100644 index 0000000..b41800a --- /dev/null +++ b/src/main/java/com/bsd/say/service/WxOpenServiceDemo.java @@ -0,0 +1,69 @@ +package com.bsd.say.service; + +import com.bsd.say.config.RedisProperies; +import me.chanjar.weixin.open.api.impl.WxOpenInRedisConfigStorage; +import me.chanjar.weixin.open.api.impl.WxOpenMessageRouter; +import me.chanjar.weixin.open.api.impl.WxOpenServiceImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.stereotype.Service; +import redis.clients.jedis.JedisPool; + +import javax.annotation.PostConstruct; + +/** + * @author 007 + */ +@Service +public class WxOpenServiceDemo extends WxOpenServiceImpl { + @Value("${wechat.aesKey}") + private String aesKey; + @Value("${wechat.componentToken}") + private String componentToken; + @Value("${wechat.appId}") + private String appId; + @Value("${wechat.componentAppId}") + private String componentAppId; + @Value("${wechat.componentAppSecret}") + private String componentAppSecret; + private Logger logger = LoggerFactory.getLogger(getClass()); + private static JedisPool pool; + private WxOpenMessageRouter wxOpenMessageRouter; + @PostConstruct + public void init() { + WxOpenInRedisConfigStorage inRedisConfigStorage = new WxOpenInRedisConfigStorage(getJedisPool()); + inRedisConfigStorage.setComponentAppId(componentAppId); + inRedisConfigStorage.setComponentAppSecret(componentAppSecret); + inRedisConfigStorage.setComponentToken(componentToken); + inRedisConfigStorage.setComponentAesKey(aesKey); + setWxOpenConfigStorage(inRedisConfigStorage); + wxOpenMessageRouter = new WxOpenMessageRouter(this); + wxOpenMessageRouter.rule().handler((wxMpXmlMessage, map, wxMpService, wxSessionManager) -> { + logger.info("\n接收到 {} 公众号请求消息,内容:{}", wxMpService.getWxMpConfigStorage().getAppId(), wxMpXmlMessage); + return null; + }).next(); + } + public WxOpenMessageRouter getWxOpenMessageRouter(){ + return wxOpenMessageRouter; + } + + private JedisPool getJedisPool() { + RedisProperies redisProperies = new RedisProperies(); + if (pool == null) { + synchronized (WxOpenServiceDemo.class) { + if (pool == null) { + pool = new JedisPool(redisProperies, redisProperies.getHost(), + redisProperies.getPort(), redisProperies.getConnectionTimeout(), + redisProperies.getSoTimeout(), redisProperies.getPassword(), + redisProperies.getDatabase(), redisProperies.getClientName(), + redisProperies.isSsl(), redisProperies.getSslSocketFactory(), + redisProperies.getSslParameters(), redisProperies.getHostnameVerifier()); + } + } + } + return pool; + } +}