Преглед на файлове

1.增加微信中间件。

tomatozq преди 5 години
родител
ревизия
8caf20882e

+ 1 - 0
pom.xml

@@ -19,6 +19,7 @@
         <module>picc-admin-server</module>
         <module>picc-common</module>
         <module>picc-enterprise-server</module>
+        <module>weixin-middleware</module>
     </modules>
     <properties>
         <java.version>1.8</java.version>

+ 155 - 0
weixin-middleware/pom.xml

@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>picc</artifactId>
+        <groupId>com.jpsoft</groupId>
+        <version>1.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>weixin-middleware</artifactId>
+    <packaging>war</packaging>
+
+    <properties>
+        <swagger2.version>2.7.0</swagger2.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.vintage</groupId>
+                    <artifactId>junit-vintage-engine</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+        <!-- swagger ui begin-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>${swagger2.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>${swagger2.version}</version>
+        </dependency>
+        <!-- swagger ui end-->
+
+
+        <!--logging start-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-logging</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.lazyluke</groupId>
+            <artifactId>log4jdbc-remix</artifactId>
+            <version>0.2.7</version>
+        </dependency>
+        <!--logging end-->
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+    </dependencies>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <fork>true</fork>
+                    <addResources>true</addResources>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.16</version>
+                <configuration>
+                    <includes>
+                        <include>**/*TestForPack.java</include>
+                    </includes>
+                    <argLine>-Xmx256M</argLine>
+                </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.surefire</groupId>
+                        <artifactId>surefire-junit47</artifactId>
+                        <version>2.16</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+    <profiles>
+        <profile>
+            <id>dev</id>
+            <dependencies>
+                <dependency>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </dependency>
+            </dependencies>
+            <properties>
+                <active.profile>dev</active.profile>
+            </properties>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+        </profile>
+        <profile>
+            <id>test</id>
+            <dependencies>
+                <dependency>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                    <scope>provided</scope>
+                </dependency>
+            </dependencies>
+            <properties>
+                <active.profile>test</active.profile>
+            </properties>
+        </profile>
+        <profile>
+            <id>production</id>
+            <properties>
+                <active.profile>production</active.profile>
+            </properties>
+            <dependencies>
+                <dependency>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+</project>

+ 18 - 0
weixin-middleware/src/main/java/com/jpsoft/weixin/WeixinMiddlewareApplication.java

@@ -0,0 +1,18 @@
+package com.jpsoft.weixin;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+@SpringBootApplication
+public class WeixinMiddlewareApplication extends SpringBootServletInitializer {
+    public static void main(String[] args) {
+        SpringApplication.run(WeixinMiddlewareApplication.class, args);
+    }
+
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
+        return builder.sources(WeixinMiddlewareApplication.class);
+    }
+}

+ 120 - 0
weixin-middleware/src/main/java/com/jpsoft/weixin/config/RedisConfig.java

@@ -0,0 +1,120 @@
+package com.jpsoft.weixin.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.interceptor.KeyGenerator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.*;
+import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import java.lang.reflect.Method;
+
+@Configuration
+@EnableCaching
+public class RedisConfig extends CachingConfigurerSupport {
+    /**
+     * 注入 RedisConnectionFactory,注意maven中要有redis.clients.jedis
+     */
+    @Autowired
+    RedisConnectionFactory redisConnectionFactory;
+
+    /**
+     * 实例化 RedisTemplate 对象
+     * @return
+     */
+    @Bean
+    public RedisTemplate<String, Object> getRedisTemplate() {
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        initDomainRedisTemplate(redisTemplate, redisConnectionFactory);
+        return redisTemplate;
+    }
+
+    /**
+     * 设置数据存入 redis 的序列化方式
+     *
+     * @param redisTemplate
+     * @param factory
+     */
+    private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) {
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
+        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
+        redisTemplate.setConnectionFactory(factory);
+    }
+
+    @Bean
+    public KeyGenerator keyGenerator() {
+        return new KeyGenerator() {
+            @Override
+            public Object generate(Object target, Method method, Object... params) {
+                StringBuilder sb = new StringBuilder();
+                sb.append(target.getClass().getName());
+                sb.append(method.getName());
+                for (Object obj : params) {
+                    sb.append(obj.toString());
+                }
+
+                return sb.toString();
+            }
+        };
+    }
+
+    /**
+     * 实例化 HashOperations 对象,可以使用 Hash 类型操作
+     *
+     * @param redisTemplate
+     * @return
+     */
+    @Bean
+    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
+        return redisTemplate.opsForHash();
+    }
+
+    /**
+     * 实例化 ValueOperations 对象,可以使用 String 操作
+     *
+     * @param redisTemplate
+     * @return
+     */
+    @Bean
+    public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
+        return redisTemplate.opsForValue();
+    }
+
+    /**
+     * 实例化 ListOperations 对象,可以使用 List 操作
+     *
+     * @param redisTemplate
+     * @return
+     */
+    @Bean
+    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
+        return redisTemplate.opsForList();
+    }
+
+    /**
+     * 实例化 SetOperations 对象,可以使用 Set 操作
+     *
+     * @param redisTemplate
+     * @return
+     */
+    @Bean
+    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
+        return redisTemplate.opsForSet();
+    }
+
+    /**
+     * 实例化 ZSetOperations 对象,可以使用 ZSet 操作
+     *
+     * @param redisTemplate
+     * @return
+     */
+    @Bean
+    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
+        return redisTemplate.opsForZSet();
+    }
+}

+ 77 - 0
weixin-middleware/src/main/java/com/jpsoft/weixin/config/SwaggerConfig.java

@@ -0,0 +1,77 @@
+package com.jpsoft.weixin.config;
+
+import io.swagger.annotations.Api;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.SecurityReference;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig {
+    @Bean
+    public Docket createRestApi() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
+                //.apis(RequestHandlerSelectors.withClassAnnotation(Controller.class))
+                .paths(PathSelectors.any())
+                .build()
+                //添加登录认证
+                .securitySchemes(securitySchemes())
+                .securityContexts(securityContexts());
+    }
+
+    private List<ApiKey> securitySchemes() {
+        //设置请求头信息
+        List<ApiKey> result = new ArrayList<>();
+        ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
+        result.add(apiKey);
+        return result;
+    }
+
+    private List<SecurityContext> securityContexts() {
+        //设置需要登录认证的路径
+        List<SecurityContext> result = new ArrayList<>();
+        result.add(getContextByPath("/*"));
+        return result;
+    }
+
+    private SecurityContext getContextByPath(String pathRegex){
+        return SecurityContext.builder()
+                .securityReferences(defaultAuth())
+                .forPaths(PathSelectors.regex(pathRegex))
+                .build();
+    }
+
+    private List<SecurityReference> defaultAuth() {
+        List<SecurityReference> result = new ArrayList<>();
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+        authorizationScopes[0] = authorizationScope;
+        result.add(new SecurityReference("Authorization", authorizationScopes));
+        return result;
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("后台接口文档")
+                .description("")
+                .termsOfServiceUrl("")
+                .version("1.0")
+                .build();
+    }
+}

+ 17 - 0
weixin-middleware/src/main/java/com/jpsoft/weixin/config/WeixinConfig.java

@@ -0,0 +1,17 @@
+package com.jpsoft.weixin.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "weixin")
+@Data
+public class WeixinConfig {
+    private String token;
+    private String appId;
+    private String appSecret;
+    private String tokenUrl;
+    private String createQrCodeUrl;
+    private String showQrCodeUrl;
+}

+ 241 - 0
weixin-middleware/src/main/java/com/jpsoft/weixin/controller/WeixinController.java

@@ -0,0 +1,241 @@
+package com.jpsoft.weixin.controller;
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.json.JSON;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.jpsoft.weixin.config.WeixinConfig;
+import com.jpsoft.weixin.util.WeixinUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.ValueOperations;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Api(description = "微信中控")
+@Controller
+@Slf4j
+public class WeixinController {
+    @Autowired
+    private WeixinConfig weixinConfig;
+
+    @Autowired
+    private ValueOperations<String,Object> valueOperations;
+
+    @ApiOperation(value="验证服务器地址的有效性")
+    @GetMapping("/")
+    @ResponseBody
+    public String index(@RequestParam(defaultValue = "") String signature,
+                        @RequestParam(defaultValue = "") String timestamp,
+                        @RequestParam(defaultValue = "") String nonce,
+                        @RequestParam(defaultValue = "") String echostr){
+        log.warn("signature=" + signature);
+        log.warn("timestamp=" + timestamp);
+        log.warn("nonce=" + nonce);
+        log.warn("echostr=" + echostr);
+
+        if (WeixinUtil.checkSignature(weixinConfig.getToken(),signature,timestamp,nonce)){
+            return echostr;
+        }
+        else{
+            return null;
+        }
+    }
+
+    @ApiOperation(value = "基础支持中获取普通access_token")
+    @GetMapping("/token")
+    @ResponseBody
+    public JSONObject getToken(@RequestParam(defaultValue = "client_credential") String grant_type,
+                        @RequestParam(defaultValue = "") String appid,
+                        @RequestParam(defaultValue = "") String secret){
+        String suffix = "_common_token";
+        JSONObject json = (JSONObject)valueOperations.get(appid + suffix);
+
+        if(json==null) {
+            StringBuilder urlBuilder = new StringBuilder();
+
+            urlBuilder.append(weixinConfig.getTokenUrl());
+
+            urlBuilder.append("?").append("grant_type=").append(grant_type);
+            urlBuilder.append("&").append("appid=").append(appid);
+            urlBuilder.append("&").append("secret=").append(secret);
+
+            try {
+                HttpGet httpGet = new HttpGet(urlBuilder.toString());
+
+                CloseableHttpClient httpClient = HttpClientBuilder.create().build();
+
+                CloseableHttpResponse response = httpClient.execute(httpGet);
+
+                HttpEntity entity = response.getEntity();
+
+                String content = EntityUtils.toString(entity);
+
+                json = new JSONObject(content);
+
+                if(json.containsKey("expires_in")){
+                    Long expiresSeconds = json.getLong("expires_in");
+                    valueOperations.set(appid + suffix,json,expiresSeconds - 30, TimeUnit.SECONDS);
+                }
+            } catch (Exception ex) {
+                log.error(ex.getMessage(), ex);
+            }
+        }
+
+        return json;
+    }
+
+    @ApiOperation(value = "处理消息事件")
+    @PostMapping("/")
+    public void processEvent(HttpServletRequest request, HttpServletResponse response){
+
+        try{
+            Map<String,Object> dataMap = WeixinUtil.xmlToMap(request.getInputStream());
+
+            //{Ticket=gQEH8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyckZDekV1RHg5SFAxczNwYjF1MWIAAgTX10teAwQsAQAA, CreateTime=1582028776, EventKey=1457, Event=SCAN, ToUserName=gh_b6e865f48dea, FromUserName=op6Frsy4xuXYGqjaJ12Xv5Q-9NtU, MsgType=event}
+            log.warn(dataMap.toString());
+
+            response.setContentType("UTF-8");
+            response.setContentType("text/html; charset=UTF-8");
+            response.setCharacterEncoding("UTF-8");
+
+            String responseText = "success";
+
+            responseText = replyTextMessage(dataMap);
+
+            response.getWriter().print(responseText);
+        }
+        catch (Exception ex){
+            log.error(ex.getMessage(),ex);
+        }
+    }
+
+    private String replyTextMessage(Map dataMap){
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("<xml>");
+        sb.append("<ToUserName><![CDATA[" + dataMap.get("FromUserName") + "]]></ToUserName>");
+        sb.append("<FromUserName><![CDATA["+ dataMap.get("ToUserName") + "]]></FromUserName>");
+        sb.append("<CreateTime>" + DateTime.now().getTime() + "</CreateTime>");
+        sb.append("<MsgType><![CDATA[text]]></MsgType>");
+        sb.append("<Content><![CDATA[你好]]></Content>");
+        sb.append("</xml>");
+
+        return sb.toString();
+    }
+
+    @ResponseBody
+    @ApiOperation(value = "创建二维码")
+    @PostMapping("/qrcode/create")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name="access_token", paramType="query", required=true, value="基础支持access_token"),
+            @ApiImplicitParam(name="expire_seconds", paramType="query", required=true, value="该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为30秒。"),
+            @ApiImplicitParam(name="action_name", paramType="query", required=true, value="二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值"),
+            @ApiImplicitParam(name="scene_id", paramType="query", value="场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1--100000)"),
+            @ApiImplicitParam(name="scene_str", paramType="query", value="场景值ID(字符串形式的ID),字符串类型,长度限制为1到64")
+    })
+    public JSONObject createQrCode(@RequestParam(name = "access_token") String accessToken,
+                               @RequestParam(name = "expire_seconds",defaultValue = "300") long expireSeconds,
+                               @RequestParam(name = "action_name",defaultValue = "QR_SCENE") String actionName,
+                               @RequestParam(name = "scene_id",required = false) String sceneId,
+                               @RequestParam(name = "scene_str",required = false) String sceneStr){
+        JSONObject resultObj = new JSONObject();
+
+        try {
+            StringBuilder urlBuilder = new StringBuilder();
+
+            urlBuilder.append(weixinConfig.getCreateQrCodeUrl())
+                    .append("?access_token=")
+                    .append(URLEncoder.encode(accessToken,"UTF-8"));
+
+            HttpPost httpPost = new HttpPost(urlBuilder.toString());
+
+            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
+
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put("expire_seconds", expireSeconds);
+            jsonObject.put("action_name", actionName);
+
+            JSONObject actionInfo = new JSONObject();
+            JSONObject scene = new JSONObject();
+
+            if(StringUtils.isNotEmpty(sceneId)) {
+                scene.put("scene_id", sceneId);
+            }
+
+            if(StringUtils.isNotEmpty(sceneStr)) {
+                scene.put("scene_str", sceneStr);
+            }
+
+            actionInfo.put("scene",scene);
+
+            jsonObject.put("action_info", actionInfo);
+
+            StringEntity entity = new StringEntity(jsonObject.toString(), "utf-8");
+
+            entity.setContentEncoding("UTF-8");
+            entity.setContentType("application/json");//发送json数据需要设置contentType
+            httpPost.setEntity(entity);
+
+            HttpResponse res = httpClient.execute(httpPost);
+
+            if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                String content = EntityUtils.toString(res.getEntity());// 返回json格式:
+                JSONObject ret = new JSONObject(content);
+
+                if (ret.containsKey("errcode")) {
+                    resultObj.put("result",false);
+                    resultObj.put("message",ret.getStr("errmsg"));
+                    resultObj.put("code",ret.getInt("errcode"));
+                }
+                else {
+                    String ticket = ret.getStr("ticket");
+                    long expire_seconds = ret.getLong("expire_seconds");
+
+                    resultObj.put("data", weixinConfig.getShowQrCodeUrl() + "?ticket=" + URLEncoder.encode(ticket, "UTF-8"));
+                    resultObj.put("result", true);
+                }
+            }
+        }
+        catch (Exception ex){
+            log.error(ex.getMessage(),ex);
+
+            resultObj.put("message",ex.getMessage());
+            resultObj.put("result",false);
+        }
+
+        return resultObj;
+    }
+}

+ 87 - 0
weixin-middleware/src/main/java/com/jpsoft/weixin/util/WeixinUtil.java

@@ -0,0 +1,87 @@
+package com.jpsoft.weixin.util;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class WeixinUtil {
+
+    public static boolean checkSignature(String token,String signature, String timestamp, String nonce) {
+        String[] arr = new String[] { token, timestamp, nonce };
+
+        String content = Arrays.stream(arr)
+                .sorted(Comparator.naturalOrder())
+                .collect(Collectors.joining());
+
+        MessageDigest md = null;
+        String tmpStr = null;
+ 
+        try {
+            md = MessageDigest.getInstance("SHA-1");
+            // 将三个参数字符串拼接成一个字符串进行sha1加密
+            byte[] digest = md.digest(content.getBytes("UTF-8"));
+
+            StringBuffer hexstr = new StringBuffer();
+
+            String shaHex = "";
+
+            for (int i = 0; i < digest.length; i++) {
+                shaHex = Integer.toHexString(digest[i] & 0xFF);
+                if (shaHex.length() < 2) {
+                    hexstr.append(0);
+                }
+                hexstr.append(shaHex);
+            }
+
+            tmpStr = hexstr.toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
+        return signature.equals(tmpStr);
+    }
+
+    public static Map<String,Object> xmlToMap(InputStream inputStream) throws Exception {
+        Map<String,Object> dataMap = new HashMap<>();
+
+        // 创建sax解析工厂
+        SAXParserFactory factory = SAXParserFactory.newInstance();
+        // 创建sax转换工具
+        SAXParser saxParser = factory.newSAXParser();
+
+        saxParser.parse(inputStream,new DefaultHandler(){
+            private String tagName;
+
+            @Override
+            public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+                tagName = qName;
+            }
+
+            @Override
+            public void endElement(String uri, String localName, String qName) throws SAXException {
+                tagName = null;
+            }
+
+            @Override
+            public void characters(char[] ch, int start, int length) throws SAXException {
+                if(tagName!=null){
+                    String content = new String(ch,start,length);
+                    dataMap.put(tagName,content);
+                }
+            }
+        });
+
+        return dataMap;
+    }
+}

+ 38 - 0
weixin-middleware/src/main/resources/application-dev.yml

@@ -0,0 +1,38 @@
+spring:
+  devtools:
+    add-properties: false
+    restart:
+      enabled: true
+  redis:
+    # Redis数据库索引(默认为0)
+    database: 1
+    # Redis服务器地址
+    host: 127.0.0.1
+    # Redis服务器连接端口
+    port: 6379
+    # Redis服务器连接密码(默认为空)
+    password:
+    # 连接池最大连接数(使用负值表示没有限制)
+    pool:
+      max-active: 8
+      # 连接池最大阻塞等待时间(使用负值表示没有限制)
+      max-wait: -1
+      # 连接池中的最大空闲连接
+      max-idle: 8
+      # 连接池中的最小空闲连接
+      min-idle: 0
+      # 连接超时时间(毫秒)
+      timeout: 0
+
+logger:
+  level: WARN
+  dir: D:\\Logs\\picc\\weixin-middleware\\
+
+weixin:
+  appId: wxc0ddd6a415c535d9
+  appSecret: 042fe6c9c970c1d9fe585dccfca89221
+  token: weixin
+  tokenUrl: "https://api.weixin.qq.com/cgi-bin/token"
+  refreshOAuth2TokenUrl: "https://api.weixin.qq.com/sns/oauth2/refresh_token"
+  createQrCodeUrl: "https://api.weixin.qq.com/cgi-bin/qrcode/create"
+  showQrCodeUrl: "https://mp.weixin.qq.com/cgi-bin/showqrcode"

+ 9 - 0
weixin-middleware/src/main/resources/application-test.yml

@@ -0,0 +1,9 @@
+spring:
+  devtools:
+    add-properties: false
+    restart:
+      enabled: true
+
+logger:
+  level: WARN
+  dir: C:\\Logs\\picc\\weixin-middleware\\

+ 15 - 0
weixin-middleware/src/main/resources/application.yml

@@ -0,0 +1,15 @@
+server:
+  port: 8086
+  servlet:
+    context-path: /weixin-middleware
+
+spring:
+  http:
+    multipart:
+      max-request-size: 20MB
+      max-file-size: 20MB
+  devtools:
+    restart:
+      enabled: true
+  profiles:
+    active: @active.profile@

+ 55 - 0
weixin-middleware/src/main/resources/logback-spring.xml

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <include resource="org/springframework/boot/logging/logback/base.xml"/>
+    <!--
+    官方文档指明,需要使用<springProperty>,才可使用application.properties(或application.yml)中的值
+    -->
+    <springProperty scope="context" name="loggerLevel" source="logger.level"/>
+    <springProperty scope="context" name="loggerPath" source="logger.dir"/>
+    <property name="pattern" value="%date %level [%thread] %logger{36} [%file : %line] %msg%n"></property>
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>${pattern}</pattern>
+        </encoder>
+    </appender>
+    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${loggerPath}/logfile.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${loggerPath}/logfile.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <MaxHistory>30</MaxHistory>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${pattern}</pattern>
+        </encoder>
+    </appender>
+    <!--
+    直接用maven的变量是@...@,用spring的变量是${...}
+    -->
+    <springProfile name="dev">
+        <!--root的level不能用变量-->
+        <logger name="jdbc" additivity="false" level="WARN">
+            <appender-ref ref="STDOUT" />
+        </logger>
+        <logger name="jdbc.sqltiming" additivity="false" level="ON">
+            <appender-ref ref="STDOUT" />
+        </logger>
+    </springProfile>
+    <springProfile name="remote">
+        <root level="WARN">
+            <appender-ref ref="STDOUT" />
+            <appender-ref ref="FILE" />
+        </root>
+    </springProfile>
+    <springProfile name="test">
+        <root level="WARN">
+            <appender-ref ref="STDOUT" />
+            <appender-ref ref="FILE" />
+        </root>
+    </springProfile>
+    <springProfile name="production">
+        <root level="WARN">
+            <appender-ref ref="STDOUT" />
+            <appender-ref ref="FILE" />
+        </root>
+    </springProfile>
+</configuration>