Bladeren bron

add(operation-backend):[wechat 信息]

wangxiao 4 jaren geleden
bovenliggende
commit
f6a65df0cc

+ 8 - 2
operation-backend/pom.xml

@@ -80,8 +80,14 @@
             <artifactId>commons-codec</artifactId>
             <version>1.9</version>
         </dependency>
-
-        <!--微信需要使用到的包 start-->
+        <!-- 仅仅是熟悉api 便使用了-->
+        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.73</version>
+        </dependency>
+        <!--微信需要使用到的包 end-->
 
 
 

+ 33 - 6
operation-backend/src/main/java/com/idiot/operationbackend/config/PlatformProperties.java

@@ -19,9 +19,19 @@ public class PlatformProperties {
     private String appId;
 
     /**
-     * 授权回调域名 配置需要和第三方平台一致
+     * appSecret
      */
-    private String authDomain;
+    private String appSecret;
+
+    /**
+     * 授权事件接收URL(用于接受验证票据)
+     */
+    private String ticketUrl;
+
+    /**
+     * 授权回调地址
+     */
+    private String authCallBack;
 
     /**
      * 加解密key
@@ -42,12 +52,29 @@ public class PlatformProperties {
         this.appId = appId;
     }
 
-    public String getAuthDomain() {
-        return authDomain;
+
+    public String getAppSecret() {
+        return appSecret;
+    }
+
+    public void setAppSecret(String appSecret) {
+        this.appSecret = appSecret;
+    }
+
+    public String getTicketUrl() {
+        return ticketUrl;
+    }
+
+    public void setTicketUrl(String ticketUrl) {
+        this.ticketUrl = ticketUrl;
+    }
+
+    public String getAuthCallBack() {
+        return authCallBack;
     }
 
-    public void setAuthDomain(String authDomain) {
-        this.authDomain = authDomain;
+    public void setAuthCallBack(String authCallBack) {
+        this.authCallBack = authCallBack;
     }
 
     public String getSecret() {

+ 26 - 0
operation-backend/src/main/java/com/idiot/operationbackend/config/RestTemplateConfig.java

@@ -0,0 +1,26 @@
+package com.idiot.operationbackend.config;
+
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.boot.web.client.RestTemplateCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+import java.time.Duration;
+
+/**
+ * RestTemplateConfig 配置 配置多个区别微服务调用和http 域名调用
+ * @author wang xiao
+ * @date Created in 17:31 2020/9/11
+ */
+@Configuration
+public class RestTemplateConfig {
+
+    @Bean("rpcRestTemplate")
+    public RestTemplate buildRpc (RestTemplateBuilder builder) {
+      return   builder.setConnectTimeout(Duration.ofSeconds(3))
+                .setReadTimeout(Duration.ofSeconds(3))
+                .build();
+    }
+
+}

+ 15 - 65
operation-backend/src/main/java/com/idiot/operationbackend/controller/WeChatController.java

@@ -19,10 +19,7 @@ import org.springframework.web.bind.annotation.*;
 import javax.servlet.http.HttpServletRequest;
 import java.io.BufferedInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+
 
 
 /**
@@ -40,34 +37,27 @@ public class WeChatController {
     @Autowired
     private WeChatService weChatService;
 
-    @Autowired
-    private PlatformProperties properties;
-
-
 
     @GetMapping("preAuth")
     @ApiOperation(value = "获取预授权码地址,微信认证公众号")
     public ResponseEntity<JsonResult<String>> getPreAuthUrl ()   {
-        return null;
+        return ResponseEntity.ok(JsonResult.success(weChatService.getPreAuthUrl()));
     }
 
 
 
 
-    @PostMapping("/authCallBack")
-    @ApiOperation(value = "第三方平台,授权事件接受url")
-    public String componentCallBack(HttpServletRequest request)  {
-
+    @PostMapping("/notice")
+    @ApiOperation(value = "验证票据接受url")
+    public String noticeCallBack(HttpServletRequest request)  {
         String nonce = request.getParameter("nonce");
         String timestamp = request.getParameter("timestamp");
         // 默认aes
         String encryptType = request.getParameter("encrypt_type");
         String msgSignature = request.getParameter("msg_signature");
-        logger.info("微信授权事件回调,回调param参数依次是nonce:{},timestamp:{},encrypt_type:{},msg_signature:{}",nonce,
+        logger.info("验证票据事件,param参数依次是nonce:{},timestamp:{},encrypt_type:{},msg_signature:{}",nonce,
                 timestamp,encryptType,msgSignature);
-
         try {
-            WXBizMsgCrypt pc = new WXBizMsgCrypt(properties.getToken(),properties.getSecret(),properties.getAppId());
             BufferedInputStream bis = new BufferedInputStream(request.getInputStream());
             ByteArrayOutputStream buf = new ByteArrayOutputStream();
             int result = bis.read();
@@ -76,62 +66,22 @@ public class WeChatController {
                 result = bis.read();
             }
             String postData = buf.toString("utf-8");
-            logger.info("微信授权事件回调,postData before decry :{}",postData);
-            postData = pc.decryptMsg(msgSignature,timestamp,nonce,postData);
-            logger.info("微信授权事件回调,postData after decry :{}",postData);
-            return weChatService.authCallBack(xmlToMap(postData));
+            logger.info("微信验证票据回调,postData before decry :{}",postData);
+            postData = weChatService.decryptMsg(msgSignature,timestamp,nonce,postData);
+            logger.info("微信验证票据回调,postData after decry :{}",postData);
+            return weChatService.notice(weChatService.xmlToMap(postData));
         }catch (Exception e) {
-            logger.info("微信授权事件回调 ------->失败,message is :{}",e.getMessage());
+            logger.info("微信验证票据回调 ------->失败,message is :{}",e.getMessage());
             return Constants.SUCCESS;
         }
     }
 
 
-
-    /**
-     *  微信消息转换成map(非加密)
-     * @author wangxiao
-     * @date 14:10 2020/9/11
-     * @param inputStream
-     * @return java.util.Map<java.lang.String,java.lang.String>
-     */
-    private Map<String, String> xmlToMap(InputStream inputStream) {
-        SAXReader saxReader = new SAXReader();
-        try {
-            Document document = saxReader.read(inputStream);
-            return parseDocument(document);
-        }catch (Exception e) {
-            logger.error("xml 转换出错");
-            return null;
-        }
+    @PostMapping("/authCallBack")
+    @ApiOperation(value = "授权地址回调")
+    public String authCallBack(HttpServletRequest request)  {
+        return "";
     }
 
 
-    /**
-     *  微信消息转换成map(非加密)
-     * @author wangxiao
-     * @date 14:10 2020/9/11
-     * @param data
-     * @return java.util.Map<java.lang.String,java.lang.String>
-     */
-    private Map<String, String> xmlToMap(String data) {
-        SAXReader saxReader = new SAXReader();
-        try {
-            Document document = saxReader.read(data);
-            return parseDocument(document);
-        }catch (Exception e) {
-            logger.error("xml 转换出错");
-            return null;
-        }
-    }
-
-    private Map<String,String> parseDocument (Document document) {
-        Map<String, String> map = new HashMap<>(10);
-        Element root = document.getRootElement();
-        List<Element> elements = root.elements();
-        for (Element element : elements) {
-            map.put(element.getName(), element.getStringValue());
-        }
-        return map;
-    }
 }

+ 11 - 5
operation-backend/src/main/java/com/idiot/operationbackend/service/facade/WeChatMessageService.java

@@ -1,13 +1,17 @@
 package com.idiot.operationbackend.service.facade;
 
+
+import com.idiot.operationbackend.support.Constants;
 import org.springframework.beans.factory.InitializingBean;
 
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.util.Map;
 
 /**
- * 微信事件接受
- * @author wang xiao
- * @date Created in 11:39 2020/9/11
+ * @author: wang xiao
+ * @description: WeChatMessageService
+ * @date: Created in 11:31 2020/6/18
  */
 public interface WeChatMessageService extends InitializingBean {
 
@@ -24,11 +28,13 @@ public interface WeChatMessageService extends InitializingBean {
     /**
      *  默认不支持消息
      * @author wangxiao
-     * @date 15:27 2020/7/1
+     * @date 15:27 2020/7/1 
      * @param param
      * @return java.lang.String
      */
     default String unSupportedMessage(Map<String,String> param) {
-      return "success";
+        return Constants.SUCCESS;
     }
+
+
 }

+ 133 - 1
operation-backend/src/main/java/com/idiot/operationbackend/service/facade/WeChatService.java

@@ -1,5 +1,15 @@
 package com.idiot.operationbackend.service.facade;
 
+import com.idiot.operationbackend.support.Constants;
+import com.idiot.operationbackend.support.wechat.AesException;
+import org.dom4j.Document;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.springframework.util.StringUtils;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -16,7 +26,129 @@ public interface WeChatService {
    * @param param
    * @return java.lang.String
    */
-    String authCallBack(Map<String,String> param);
+    String notice(Map<String,String> param);
+
+
+  /**
+   * 不支持的消息
+   * @author wangxiao
+   * @date 15:27 2020/7/1
+   * @param param
+   * @return java.lang.String
+   */
+  default String unSupportedMessage(Map<String,String> param) {
+    return Constants.SUCCESS;
+  }
+
+
+
+  /**
+   *  获取令牌
+   * @author wangxiao
+   * @date 17:25 2020/9/11
+   * @param
+   * @return java.lang.String
+   */
+  String getComponentAccessToken ();
+
+
+  /**
+   *  获取预授权码
+   * @author wangxiao
+   * @date 18:10 2020/9/11
+   * @return java.lang.String
+   */
+  String getPreAuthCode ();
+
+
+  /**
+   *  获取预授权地址
+   * @author wangxiao
+   * @date 19:34 2020/9/11
+   * @param
+   * @return java.lang.String
+   */
+  String getPreAuthUrl ();
 
 
+
+  /**
+   *  微信解密数据
+   * @author wangxiao
+   * @date 19:41 2020/9/11
+   * @param msgSignature
+   * @param timestamp
+   * @param nonce
+   * @param data
+   * @return java.lang.String
+   */
+  String decryptMsg(String msgSignature,String timestamp,String nonce,String data);
+
+
+  /**
+   *  微信加密数据
+   * @author wangxiao
+   * @date 19:49 2020/9/11
+   * @param data
+   * @return java.lang.String
+   */
+  String encryptMsg(String data);
+
+
+
+  /**
+   *  微信消息转换成map(非加密)
+   * @author wangxiao
+   * @date 14:10 2020/9/11
+   * @param inputStream
+   * @return java.util.Map<java.lang.String,java.lang.String>
+   */
+  default Map<String, String> xmlToMap(InputStream inputStream) {
+    SAXReader saxReader = new SAXReader();
+    try {
+      Document document = saxReader.read(inputStream);
+      return parseDocument(document);
+    }catch (Exception e) {
+      return null;
+    }
+  }
+
+
+  /**
+   *  微信消息转换成map(非加密)
+   * @author wangxiao
+   * @date 14:10 2020/9/11
+   * @param data
+   * @return java.util.Map<java.lang.String,java.lang.String>
+   */
+  default Map<String, String> xmlToMap(String data) {
+    if (StringUtils.isEmpty(data)) {
+      return null;
+    }
+    SAXReader saxReader = new SAXReader();
+    try {
+      Document document = saxReader.read(data);
+      return parseDocument(document);
+    }catch (Exception e) {
+      return null;
+    }
+  }
+
+  /**
+   *  document 转 map
+   * @author wangxiao
+   * @date 19:36 2020/9/11
+   * @param document
+   * @return java.util.Map<java.lang.String,java.lang.String>
+   */
+  default Map<String,String> parseDocument (Document document) {
+    Map<String, String> map = new HashMap<>(10);
+    Element root = document.getRootElement();
+    List<Element> elements = root.elements();
+    for (Element element : elements) {
+      map.put(element.getName(), element.getStringValue());
+    }
+    return map;
+  }
+
 }

+ 11 - 7
operation-backend/src/main/java/com/idiot/operationbackend/service/impl/EventMessageServiceImpl.java

@@ -26,19 +26,23 @@ public class EventMessageServiceImpl implements WeChatMessageService {
 
 
 
-
-
     private final Logger logger = LoggerFactory.getLogger(EventMessageServiceImpl.class);
 
 
     @Override
     public void afterPropertiesSet()  {
+
+        logger.info("微信消息,事件处理类--------------->正在初始化");
+        synchronized (eventMap) {
+            eventMap.put("subscribe",this::subscribeEvent);
+            eventMap.put("SCAN",this::scanEvent);
+            eventMap.put("LOCATION",this::locationEvent);
+            eventMap.put("CLICK",this::clickEvent);
+            eventMap.put("VIEW",this::viewEvent);
+        }
+        logger.info("微信消息,事件处理类--------------->初始化完成");
         WeChatMessageFactory.addService("event",this);
-        eventMap.put("subscribe",this::subscribeEvent);
-        eventMap.put("SCAN",this::scanEvent);
-        eventMap.put("LOCATION",this::locationEvent);
-        eventMap.put("CLICK",this::clickEvent);
-        eventMap.put("VIEW",this::viewEvent);
+
     }
 
 

+ 1 - 0
operation-backend/src/main/java/com/idiot/operationbackend/service/impl/TextMessageServiceImpl.java

@@ -4,6 +4,7 @@ package com.idiot.operationbackend.service.impl;
 
 import com.idiot.operationbackend.handler.WeChatMessageFactory;
 import com.idiot.operationbackend.service.facade.WeChatMessageService;
+import com.idiot.operationbackend.service.facade.WeChatService;
 import org.springframework.stereotype.Service;
 
 import java.util.Map;

+ 128 - 5
operation-backend/src/main/java/com/idiot/operationbackend/service/impl/WeChatServiceImpl.java

@@ -1,12 +1,28 @@
 package com.idiot.operationbackend.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.cache.*;
+import com.idiot.operationbackend.config.PlatformProperties;
 import com.idiot.operationbackend.service.facade.WeChatService;
 import com.idiot.operationbackend.support.Constants;
+import com.idiot.operationbackend.support.wechat.AesException;
+import com.idiot.operationbackend.support.wechat.WXBizMsgCrypt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.*;
 import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
 
+import javax.annotation.Resource;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
 
 /**
  * @author wang xiao
@@ -15,19 +31,126 @@ import java.util.Objects;
 @Service
 public class WeChatServiceImpl implements WeChatService, InitializingBean {
 
-    @Override
-    public String authCallBack(Map<String, String> param) {
 
+    private final Logger logger = LoggerFactory.getLogger(WeChatServiceImpl.class);
+
+    private final Cache<String,String> cache = CacheBuilder.newBuilder()
+            .concurrencyLevel(8)
+            .expireAfterWrite(100, TimeUnit.MINUTES)
+            .initialCapacity(10)
+            .maximumSize(100)
+            .recordStats()
+            .removalListener(notification -> logger.warn("cache {} was removed, cause is {}" ,notification.getKey(), notification.getCause()))
+            .build();
+
+    private WXBizMsgCrypt pc;
+
+    @Resource(name = "rpcRestTemplate")
+    private RestTemplate restTemplate;
+
+
+    @Autowired
+    private PlatformProperties properties;
+
+
+    @Override
+    public String notice(Map<String, String> param) {
         if (Objects.isNull(param)) {
             return Constants.SUCCESS;
         }
-        String appId = param.get("AppId");
         String infoType = param.get("InfoType");
-        return null;
+        String appId = param.get("AppId");
+        String ticket = param.get("ComponentVerifyTicket");
+        logger.info("接受微信验证票据数据,infoType:{},appId:{},ticket:{}",infoType,appId,ticket);
+        cache.put(String.format("component_verify_ticket_%s",appId),ticket);
+        return Constants.SUCCESS;
     }
 
+
     @Override
-    public void afterPropertiesSet() throws Exception {
+    public String decryptMsg(String msgSignature, String timestamp, String nonce, String data)  {
+        try {
+            return pc.decryptMsg(msgSignature,timestamp,nonce,data);
+        }catch (AesException e){
+            logger.error("微信解密数据发生错误----->msgSignature:{},timestamp:{},nonce:{},data:{}",msgSignature,
+                    timestamp,nonce,data);
+            return null;
+        }
+    }
 
+    @Override
+    public String encryptMsg(String data) {
+        String timestamp =  Long.toString(System.currentTimeMillis());
+        String nonce = pc.getRandomStr();
+        try {
+            return  pc.encryptMsg(data,timestamp,nonce);
+        }catch (AesException e){
+            logger.error("微信加密数据发生错误----->timestamp:{},nonce:{},data:{}",timestamp,nonce,data);
+            return null;
+        }
     }
+
+    @Override
+    public String getComponentAccessToken() {
+        String appId = properties.getAppId();
+        String componentAccessToken = cache.getIfPresent(String.format("component_access_token_%s",appId));
+        if ( !StringUtils.isEmpty(componentAccessToken)) {
+            return componentAccessToken;
+        }
+        String requestUrl = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";
+        HttpHeaders headers = new HttpHeaders();
+        MultiValueMap<String,String> params = new LinkedMultiValueMap<>(3);
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        String ticket = cache.getIfPresent(String.format("component_verify_ticket_%s",appId));
+        params.add("component_appid",appId);
+        params.add("component_appsecret",properties.getAppSecret());
+        params.add("component_verify_ticket",ticket);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);
+        ResponseEntity<String> response = restTemplate.exchange(requestUrl, HttpMethod.POST, requestEntity, String.class);
+        String  jsonStr = response.getBody();
+        logger.info("请求微信获取令牌component_access_token 请求appId:{},ticket:{},微信返回结果:{}",appId,ticket,jsonStr);
+        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
+        componentAccessToken = jsonObject.getString("component_access_token");
+        cache.put(String.format("component_access_token_%s",appId),componentAccessToken);
+        return componentAccessToken;
+    }
+
+    @Override
+    public String getPreAuthCode() {
+        String appId = properties.getAppId();
+        String requestUrl =  "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=COMPONENT_ACCESS_TOKEN";
+        String componentAccessToken = getComponentAccessToken();
+        requestUrl = requestUrl.replace("COMPONENT_ACCESS_TOKEN",componentAccessToken);
+        HttpHeaders headers = new HttpHeaders();
+        MultiValueMap<String,String> params = new LinkedMultiValueMap<>(1);
+        params.add("component_appid",appId);
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);
+        ResponseEntity<String> response = restTemplate.exchange(requestUrl, HttpMethod.POST, requestEntity, String.class);
+        String  jsonStr = response.getBody();
+        logger.info("请求微信获取预授权码 请求appId:{},微信返回结果:{}",appId,jsonStr);
+        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
+        return jsonObject.getString("pre_auth_code");
+    }
+
+    @Override
+    public String getPreAuthUrl() {
+        String preAuthUrl = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=APPID&pre_auth_code=PREAUTHCODE&redirect_uri=URI&auth_type=1";
+        String appId = properties.getAppId();
+        String preAuthCode = getPreAuthCode();
+        preAuthUrl = preAuthUrl.replace("APPID",appId).replace("PREAUTHCODE",preAuthCode);
+        logger.info("生成预授权地址, preAuthUrl is {}",preAuthUrl);
+        return preAuthUrl;
+    }
+
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        logger.info("WeChatService类--------------->正在初始化");
+        pc = new WXBizMsgCrypt(properties.getToken(), properties.getSecret(), properties.getAppId());
+        logger.info("cache size  is {}",cache.size());
+        logger.info("WeChatService类--------------->初始化完成");
+    }
+
+
 }

+ 1 - 1
operation-backend/src/main/java/com/idiot/operationbackend/support/wechat/WXBizMsgCrypt.java

@@ -86,7 +86,7 @@ public class WXBizMsgCrypt {
 	}
 
 	// 随机生成16位字符串
-	String getRandomStr() {
+	public String getRandomStr() {
 		String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 		Random random = new Random();
 		StringBuffer sb = new StringBuffer();

+ 7 - 3
operation-backend/src/main/resources/application-dev.yml

@@ -52,9 +52,13 @@ wechat:
   platform:
     # appid
     appId: xxx
-    # 授权回调url
-    authDomain: https:xxxxxx/wechat/authCallBack
+    # appsecret
+    appSecret: xxxxx
+    # 授权事件接收URL
+    ticketUrl: https:xxxxxx/wechat/notice
     # 加解密key
     secret: xxxxx
     # 开发平台配置token
-    token: xxxxxx
+    token: xxxxxx
+    # authCallBackUrl 授权回调地址
+    authCallBack: https://luojigou.vip/v1/api/wechat/authCallBack