Parcourir la source

redis缓存配置 防止重复请求攻击拦截 微信支付(未测试) 百度翻译接口

zhs il y a 3 ans
Parent
commit
fd21c5da03

+ 3 - 0
build.gradle

@@ -38,6 +38,9 @@ dependencies {
     implementation 'ws.schild:jave-core:3.1.1'
     implementation 'ws.schild:jave-nativebin-win64:3.1.1'
     implementation 'commons-fileupload:commons-fileupload:1.3.2'
+    implementation 'com.github.wechatpay-apiv3:wechatpay-apache-httpclient:0.3.0'//微信支付
+    implementation 'org.apache.httpcomponents:httpclient:4.3.2'  //post get请求包
+    implementation 'com.baidubce:api-explorer-sdk:1.0.3.1'//百度翻译
     implementation fileTree(dir:'lib',includes:['*jar'])
     annotationProcessor 'org.projectlombok:lombok'
     developmentOnly 'org.springframework.boot:spring-boot-devtools'

+ 65 - 0
src/main/java/com/travel/baidu/Base64Util.java

@@ -0,0 +1,65 @@
+package com.travel.baidu;
+
+/**
+ * Base64 工具类
+ */
+public class Base64Util {
+    private static final char last2byte = (char) Integer.parseInt("00000011", 2);
+    private static final char last4byte = (char) Integer.parseInt("00001111", 2);
+    private static final char last6byte = (char) Integer.parseInt("00111111", 2);
+    private static final char lead6byte = (char) Integer.parseInt("11111100", 2);
+    private static final char lead4byte = (char) Integer.parseInt("11110000", 2);
+    private static final char lead2byte = (char) Integer.parseInt("11000000", 2);
+    private static final char[] encodeTable = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
+
+    public Base64Util() {
+    }
+
+    public static String encode(byte[] from) {
+        StringBuilder to = new StringBuilder((int) ((double) from.length * 1.34D) + 3);
+        int num = 0;
+        char currentByte = 0;
+
+        int i;
+        for (i = 0; i < from.length; ++i) {
+            for (num %= 8; num < 8; num += 6) {
+                switch (num) {
+                    case 0:
+                        currentByte = (char) (from[i] & lead6byte);
+                        currentByte = (char) (currentByte >>> 2);
+                    case 1:
+                    case 3:
+                    case 5:
+                    default:
+                        break;
+                    case 2:
+                        currentByte = (char) (from[i] & last6byte);
+                        break;
+                    case 4:
+                        currentByte = (char) (from[i] & last4byte);
+                        currentByte = (char) (currentByte << 2);
+                        if (i + 1 < from.length) {
+                            currentByte = (char) (currentByte | (from[i + 1] & lead2byte) >>> 6);
+                        }
+                        break;
+                    case 6:
+                        currentByte = (char) (from[i] & last2byte);
+                        currentByte = (char) (currentByte << 4);
+                        if (i + 1 < from.length) {
+                            currentByte = (char) (currentByte | (from[i + 1] & lead4byte) >>> 4);
+                        }
+                }
+
+                to.append(encodeTable[currentByte]);
+            }
+        }
+
+        if (to.length() % 4 != 0) {
+            for (i = 4 - to.length() % 4; i > 0; --i) {
+                to.append("=");
+            }
+        }
+
+        return to.toString();
+    }
+}

+ 72 - 0
src/main/java/com/travel/baidu/FileUtil.java

@@ -0,0 +1,72 @@
+package com.travel.baidu;
+
+import java.io.*;
+
+/**
+ * 文件读取工具类
+ */
+public class FileUtil {
+
+    /**
+     * 读取文件内容,作为字符串返回
+     */
+    public static String readFileAsString(String filePath) throws IOException {
+        File file = new File(filePath);
+        if (!file.exists()) {
+            throw new FileNotFoundException(filePath);
+        } 
+
+        if (file.length() > 1024 * 1024 * 1024) {
+            throw new IOException("File is too large");
+        } 
+
+        StringBuilder sb = new StringBuilder((int) (file.length()));
+        // 创建字节输入流  
+        FileInputStream fis = new FileInputStream(filePath);  
+        // 创建一个长度为10240的Buffer
+        byte[] bbuf = new byte[10240];  
+        // 用于保存实际读取的字节数  
+        int hasRead = 0;  
+        while ( (hasRead = fis.read(bbuf)) > 0 ) {  
+            sb.append(new String(bbuf, 0, hasRead));  
+        }  
+        fis.close();  
+        return sb.toString();
+    }
+
+    /**
+     * 根据文件路径读取byte[] 数组
+     */
+    public static byte[] readFileByBytes(String filePath) throws IOException {
+        File file = new File(filePath);
+        if (!file.exists()) {
+            throw new FileNotFoundException(filePath);
+        } else {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
+            BufferedInputStream in = null;
+
+            try {
+                in = new BufferedInputStream(new FileInputStream(file));
+                short bufSize = 1024;
+                byte[] buffer = new byte[bufSize];
+                int len1;
+                while (-1 != (len1 = in.read(buffer, 0, bufSize))) {
+                    bos.write(buffer, 0, len1);
+                }
+
+                byte[] var7 = bos.toByteArray();
+                return var7;
+            } finally {
+                try {
+                    if (in != null) {
+                        in.close();
+                    }
+                } catch (IOException var14) {
+                    var14.printStackTrace();
+                }
+
+                bos.close();
+            }
+        }
+    }
+}

+ 29 - 0
src/main/java/com/travel/baidu/GsonUtils.java

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 Baidu, Inc. All Rights Reserved.
+ */
+package com.travel.baidu;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParseException;
+
+import java.lang.reflect.Type;
+
+/**
+ * Json工具类.
+ */
+public class GsonUtils {
+    private static Gson gson = new GsonBuilder().create();
+
+    public static String toJson(Object value) {
+        return gson.toJson(value);
+    }
+
+    public static <T> T fromJson(String json, Class<T> classOfT) throws JsonParseException {
+        return gson.fromJson(json, classOfT);
+    }
+
+    public static <T> T fromJson(String json, Type typeOfT) throws JsonParseException {
+        return (T) gson.fromJson(json, typeOfT);
+    }
+}

+ 77 - 0
src/main/java/com/travel/baidu/HttpUtil.java

@@ -0,0 +1,77 @@
+package com.travel.baidu;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * http 工具类
+ */
+public class HttpUtil {
+
+    public static String post(String requestUrl, String accessToken, String params)
+            throws Exception {
+        String contentType = "application/x-www-form-urlencoded";
+        return HttpUtil.post(requestUrl, accessToken, contentType, params);
+    }
+
+    public static String post(String requestUrl, String accessToken, String contentType, String params)
+            throws Exception {
+        String encoding = "UTF-8";
+        if (requestUrl.contains("nlp")) {
+            encoding = "GBK";
+        }
+        return HttpUtil.post(requestUrl, accessToken, contentType, params, encoding);
+    }
+
+    public static String post(String requestUrl, String accessToken, String contentType, String params, String encoding)
+            throws Exception {
+        String url = requestUrl + "?access_token=" + accessToken;
+        return HttpUtil.postGeneralUrl(url, contentType, params, encoding);
+    }
+
+    public static String postGeneralUrl(String generalUrl, String contentType, String params, String encoding)
+            throws Exception {
+        URL url = new URL(generalUrl);
+        // 打开和URL之间的连接
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        connection.setRequestMethod("POST");
+        // 设置通用的请求属性
+        connection.setRequestProperty("Content-Type", contentType);
+        connection.setRequestProperty("Connection", "Keep-Alive");
+        connection.setUseCaches(false);
+        connection.setDoOutput(true);
+        connection.setDoInput(true);
+
+        // 得到请求的输出流对象
+        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
+        out.write(params.getBytes(encoding));
+        out.flush();
+        out.close();
+
+        // 建立实际的连接
+        connection.connect();
+        // 获取所有响应头字段
+        Map<String, List<String>> headers = connection.getHeaderFields();
+        // 遍历所有的响应头字段
+        for (String key : headers.keySet()) {
+            System.err.println(key + "--->" + headers.get(key));
+        }
+        // 定义 BufferedReader输入流来读取URL的响应
+        BufferedReader in = null;
+        in = new BufferedReader(
+                new InputStreamReader(connection.getInputStream(), encoding));
+        String result = "";
+        String getLine;
+        while ((getLine = in.readLine()) != null) {
+            result += getLine;
+        }
+        in.close();
+        System.err.println("result:" + result);
+        return result;
+    }
+}

+ 94 - 0
src/main/java/com/travel/baidu/Test.java

@@ -0,0 +1,94 @@
+package com.travel.baidu;
+
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Controller
+@RequestMapping("translationInterface")
+public class Test {
+    /**
+     * 翻译接口
+     * @param translationVal 参数
+     * @return 返回翻译结果
+     */
+    @RequestMapping("translation")
+    @ResponseBody
+    public String translation(String translationVal){
+        // 请求url
+        String url = "https://aip.baidubce.com/rpc/2.0/mt/texttrans/v1";
+
+        try {
+            Map<String, Object> map = new HashMap<>();
+            map.put("from", "zh");
+            map.put("to", "en");
+            map.put("q", "这是一次测试用例,测试成功与否");
+            map.put("termIds", "bak,pl");
+            String param = GsonUtils.toJson(map);
+            // 注意这里仅为了简化编码每一次请求都去获取access_token,线上环境access_token有过期时间, 客户端可自行缓存,过期后重新获取。
+            String accessToken = accessToken();
+            String result = HttpUtil.post(url, accessToken, "application/json", param);
+            System.out.println(result);
+            return result;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+    /**
+     * 百度翻译获取access_token
+     * @return 返回access_token
+     */
+    public static String accessToken(){
+        String baidukey = "xmziCjk32tDLk9U2jpY2F5E7";
+        String secretKey = "lYpEeG2tKKS82KZ9Q1gHKpfGgrRAXQMQ";
+        // 获取token地址
+        String authHost = "https://aip.baidubce.com/oauth/2.0/token?";
+        String getAccessTokenUrl = authHost
+                // 1. grant_type为固定参数
+                + "grant_type=client_credentials"
+                // 2. 官网获取的 API Key
+                + "&client_id=" + baidukey
+                // 3. 官网获取的 Secret Key
+                + "&client_secret=" + secretKey;
+        try {
+            URL realUrl = new URL(getAccessTokenUrl);
+            // 打开和URL之间的连接
+            HttpURLConnection connection = (HttpURLConnection) realUrl.openConnection();
+            connection.setRequestMethod("GET");
+            connection.connect();
+            // 获取所有响应头字段
+            Map<String, List<String>> map = connection.getHeaderFields();
+            // 遍历所有的响应头字段
+            for (String key : map.keySet()) {
+                System.err.println(key + "--->" + map.get(key));
+            }
+            // 定义 BufferedReader输入流来读取URL的响应
+            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+            String result = "";
+            String line;
+            while ((line = in.readLine()) != null) {
+                result += line;
+            }
+            /**
+             * 返回结果示例
+             */
+            JSONObject jsonObject = JSONObject.parseObject(result);
+            System.out.println(jsonObject);
+            String access_token = jsonObject.getString("access_token");
+            return access_token;
+        } catch (Exception e) {
+            System.err.printf("获取token失败!");
+            e.printStackTrace(System.err);
+        }
+        return null;
+    }
+}

+ 11 - 1
src/main/java/com/travel/config/InterceptConfig.java

@@ -1,13 +1,22 @@
 package com.travel.config;
 
 import com.travel.util.JWTIntercept;
+import com.travel.util.SessionInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.config.annotation.CorsRegistry;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
+/**
+ * 拦截器跟跨域解决 token拦截器
+ */
 @Configuration
 public class InterceptConfig implements WebMvcConfigurer {
+
+    @Autowired
+    private SessionInterceptor sessionInterceptor;
+
     /**
      * 重写父类提供的跨域请求处理的接口
      *
@@ -32,8 +41,9 @@ public class InterceptConfig implements WebMvcConfigurer {
 
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(sessionInterceptor);// 多次请求监听拦截
         registry.addInterceptor(new JWTIntercept())
-                .addPathPatterns("/**")   // 正常情况下 拦截所有请求/**,因测试需要就先拦截/user/test
+                .addPathPatterns("/**")   //拦截所有请求/**
                 .excludePathPatterns("/personal/*").excludePathPatterns("/supplier/*");  //放心登录接口,因为要通过登录获取token
     }
 }

+ 2 - 0
src/main/java/com/travel/controller/PersonalController.java

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.travel.jwt.JWTUtil;
 import com.travel.model.User;
 import com.travel.service.LoginService;
+import com.travel.util.AccessLimit;
 import com.travel.util.requsetMap;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -150,6 +151,7 @@ public class PersonalController {
      */
     @GetMapping("updateUser")
     @ResponseBody
+    @AccessLimit(seconds = 5, maxCount = 5)
     public Map<String, Object> updateUser(User user, HttpServletResponse response){
         response.setHeader("Access-Control-Allow-Origin", "*");
         Map<String, Object> map = new HashMap<>();

+ 95 - 0
src/main/java/com/travel/controller/WechatPay.java

@@ -0,0 +1,95 @@
+package com.travel.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.travel.wxPay.PayBeanConfig;
+import com.travel.wxPay.WechatPayApi;
+import com.travel.wxPay.WxPayConfigure;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import java.text.SimpleDateFormat;
+import org.apache.http.client.methods.HttpPost;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+@RestController
+@RequestMapping("wxPay")
+@Slf4j
+public class WechatPay {
+
+//    @Autowired
+//    private PayBeanConfig payBeanConfig;
+
+    @Autowired
+    private WxPayConfigure payConfig;
+//
+//    @Autowired
+//    private CloseableHttpClient wechatPayClient;
+
+    @PostMapping("wxPayPost")
+    public Map<String, Object> wxPayPost(String amount){
+        Map<String, Object> map = new HashMap<>();
+        JSONObject payObj = new JSONObject();
+
+        payObj.put("mchid", payConfig.getMchId());
+        payObj.put("out_trade_no", getOrderNo());
+        payObj.put("appid", payConfig.getWxPayAppid());
+        payObj.put("description", "王师傅的红包");
+        payObj.put("notify_url", payConfig.getCallbackUrl());
+
+        //订单总金额,单位为分。
+        JSONObject amountObj = new JSONObject();
+        amountObj.put("total", amount);
+        amountObj.put("currency","CNY");
+        payObj.put("amount", amountObj);
+
+        String body = payObj.toJSONString();
+
+        log.info("请求参数:{}",body);
+
+        StringEntity entity = new StringEntity(body,"utf-8");
+        entity.setContentType("application/json");
+
+        HttpPost httpPost = new HttpPost(WechatPayApi.NATIVE_ORDER);
+        httpPost.setHeader("Accept","application/json");
+        httpPost.setEntity(entity);
+
+//        try(CloseableHttpResponse response = wechatPayClient.execute(httpPost)){
+//
+//            //响应码
+//            int statusCode = response.getStatusLine().getStatusCode();
+//            //响应体
+//            String responseStr = EntityUtils.toString(response.getEntity());
+//
+//            log.info("下单响应码:{},响应体:{}",statusCode,responseStr);
+//
+//        }catch (Exception e){
+//            e.printStackTrace();
+//        }
+
+        return map;
+    }
+
+    /**
+     * 生成订单号
+     * @return
+     */
+    public static String getOrderNo() {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
+        String newDate = sdf.format(new Date());
+        String result = "";
+        Random random = new Random();
+        for (int i = 0; i < 3; i++) {
+            result += random.nextInt(10);
+        }
+        return newDate + result;
+    }
+}

+ 71 - 103
src/main/java/com/travel/util/SessionInterceptor.java

@@ -1,103 +1,71 @@
-//package com.travel.util;
-//
-//import com.alibaba.fastjson.JSONObject;
-//import org.apache.commons.logging.Log;
-//import org.apache.commons.logging.LogFactory;
-//import org.springframework.stereotype.Component;
-//import org.springframework.web.method.HandlerMethod;
-//import org.springframework.web.servlet.ModelAndView;
-//import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
-//import javax.annotation.Resource;
-//import javax.servlet.http.HttpServletRequest;
-//import javax.servlet.http.HttpServletResponse;
-//
-///**
-// * @author :zzb
-// * @desc : 接口拦截器
-// */
-//@Component
-//@SuppressWarnings("all")
-//public class APIInterceptor extends HandlerInterceptorAdapter {
-//
-//    private final Log log = LogFactory.getLog(APIInterceptor.class);
-//
-////    @Resource
-////    private RedisUtil.redisString redisString;
-//
-//    /**
-//     * 预处理回调方法,实现处理器的预处理(如登陆检查/判断同一对象短时间内是否重复调用接口等) 第三个参数为相应的处理器即controller
-//     * f返回true表示流程继续,调用下一个拦截器或者处理器,返回false表示流程中断,通过response产生响应
-//     */
-//    @Override
-//    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-//            throws Exception {
-//
-//        //判断同一用户短时间内是否重复请求接口
-//        log.info("========================request path==============================>"+ request.getRequestURI());
-//        //判断请求是否属于方法的请求
-//        if(handler instanceof HandlerMethod){
-//            HandlerMethod hm = (HandlerMethod) handler;
-//            //获取方法中的注解,看是否有该注解
-//            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
-//            log.info("========================accessLimit==============================>"+ accessLimit);
-//            if(accessLimit == null){
-//                return true;
-//            }
-//            int seconds = accessLimit.seconds();
-//            int maxCount = accessLimit.maxCount();
-//            boolean login = accessLimit.needLogin();
-//            //如果需要登录
-//            if(login){
-//                //获取登录的session进行判断
-//                log.info("------------------------需要登录 ----------->"+request.getRemoteAddr());
-//            }
-//
-//            String ip=request.getRemoteAddr();
-//            String key = request.getServletPath() + ":" + ip ;
-////            Integer count = (Integer) redisString.get(key);
-////            if (null == count || -1 == count) {
-////                redisString.set(key, 1,seconds);
-////                return true;
-////            }
-////
-////            if (count < maxCount) {
-////                count = count+1;
-////                redisString.set(key, count,seconds);
-////                return true;
-////            }
-//
-////            if (count >= maxCount) {
-////                //response 返回 json 请求过于频繁请稍后再试
-////                ResultBean resultBean = new ResultBean(9999,"操作过于频繁");
-////                response.setCharacterEncoding("UTF-8");
-////                response.setContentType("application/json; charset=utf-8");
-////                Object obj = JSONObject.toJSON(resultBean);
-////                response.getWriter().write(JSONObject.toJSONString(obj));
-////                return false;
-////            }
-//        }
-//
-//        return super.preHandle(request, response, handler);
-//    }
-//
-//
-//    /**
-//     * 当请求进行处理之后,也就是controller方法调用之后执行,但是他会在DispatcherServlet进行视图渲染之前被调用
-//     * 此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理
-//     */
-//    @Override
-//    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
-//                           ModelAndView modelAndView) throws Exception {
-//
-//    }
-//
-//    /**
-//     * 方法将在整个请求结束之后,也就是DispatcheServlet进行视图渲染之后执行,这个方法的主要作用是对资源的清理工作
-//     */
-//    @Override
-//    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
-//            throws Exception {
-//
-//    }
-//
-//}
+package com.travel.util;
+
+import com.alibaba.fastjson.JSONObject;
+import org.mybatis.logging.Logger;
+import org.mybatis.logging.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 监听访问次数 防止多次访问攻击
+ */
+@Component
+public class SessionInterceptor extends HandlerInterceptorAdapter {
+
+    private static final Logger logger = LoggerFactory.getLogger(SessionInterceptor.class);
+
+    @Autowired
+    private RedisUtil redisUtil;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+
+        HandlerMethod hm = (HandlerMethod) handler;
+        //获取方法中的注解,看是否有该注解
+        AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
+        if(accessLimit != null){
+            int seconds = accessLimit.seconds();
+            int maxCount = accessLimit.maxCount();
+
+            //获取用户ip
+            String ip = request.getHeader("x-forwarded-for");      // 有可能ip是代理的
+            if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getHeader("Proxy-Client-IP");
+            }
+            if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getHeader("WL-Proxy-Client-IP");
+            }
+            if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getRemoteAddr();
+            }
+
+            //从redis中获取用户访问的次数
+            Integer count = Integer.parseInt(redisUtil.get(ip) == null ? "0" : redisUtil.get(ip));
+            if(count == 0){
+                //第一次访问
+                redisUtil.add(ip, 1, seconds, TimeUnit.SECONDS);
+            }else if(count < maxCount){
+                //加1
+                count = count + 1;
+                redisUtil.add(ip, count, seconds, TimeUnit.SECONDS);
+            }else{
+                //超出访问次数
+                System.out.println("访问过快ip  ===> " + ip + " 且在   " + seconds + " 秒内超过最大限制  ===> " + maxCount + " 次数达到    ====> " + count);
+                response.setCharacterEncoding("UTF-8");
+                response.setContentType("application/json; charset=utf-8");
+                JSONObject result = new JSONObject();
+                result.put("msg", "500");
+                result.put("errMsg", "操作太快");
+                Object obj = JSONObject.toJSON(result);
+                response.getWriter().write(JSONObject.toJSONString(obj));
+                return false;
+            }
+        }
+        return super.preHandle(request, response, handler);
+    }
+}

+ 103 - 0
src/main/java/com/travel/wxPay/PayBeanConfig.java

@@ -0,0 +1,103 @@
+package com.travel.wxPay;
+
+import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
+import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
+import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
+import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
+import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ClassPathResource;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+import java.util.stream.Collectors;
+
+//@Configuration
+@Slf4j
+public class PayBeanConfig {
+    @Autowired
+    private WxPayConfigure payConfig;
+
+
+    /**
+     * 加载秘钥
+     *
+     * @return
+     * @throws IOException
+     */
+    public PrivateKey getPrivateKey() throws IOException {
+        InputStream inputStream = new ClassPathResource(payConfig.getPrivateKeyPath()
+                .replace("classpath:", "")).getInputStream();
+
+        String content = new BufferedReader(new InputStreamReader(inputStream))
+                .lines().collect(Collectors.joining(System.lineSeparator()));
+
+        try {
+            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
+                    .replace("-----END PRIVATE KEY-----", "")
+                    .replaceAll("\\s+", "");
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+
+            PrivateKey finalPrivateKey = kf.generatePrivate(
+                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
+
+            return finalPrivateKey;
+
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("当前Java环境不支持RSA", e);
+        } catch (InvalidKeySpecException e) {
+            throw new RuntimeException("无效的密钥格式");
+        }
+    }
+
+
+    /**
+     * 定时获取微信签名验证器,自动获取微信平台证书(证书里面包括微信平台公钥)
+     *
+     * @return
+     */
+    @Bean
+    public ScheduledUpdateCertificatesVerifier getCertificatesVerifier() throws IOException {
+
+        // 使用定时更新的签名验证器,不需要传入证书
+        ScheduledUpdateCertificatesVerifier verifier = null;
+        verifier = new ScheduledUpdateCertificatesVerifier(
+                new WechatPay2Credentials(payConfig.getMchId(),
+                        new PrivateKeySigner(payConfig.getMchSerialNo(),
+                                getPrivateKey())),
+                payConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
+
+        return verifier;
+    }
+
+
+    /**
+     * 获取http请求对象,会自动的处理签名和验签,
+     * 并进行证书自动更新
+     *
+     * @return
+     */
+    @Bean("wechatPayClient")
+    public CloseableHttpClient getWechatPayClient(ScheduledUpdateCertificatesVerifier verifier) throws IOException {
+        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
+                .withMerchant(payConfig.getMchId(),payConfig.getMchSerialNo() , getPrivateKey())
+                .withValidator(new WechatPay2Validator(verifier));
+
+        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
+        CloseableHttpClient httpClient = builder.build();
+
+        return httpClient;
+    }
+}

+ 38 - 0
src/main/java/com/travel/wxPay/WechatPayApi.java

@@ -0,0 +1,38 @@
+package com.travel.wxPay;
+
+public class WechatPayApi {
+    /**
+     * 微信支付主机地址
+     */
+    public static final String HOST = "https://api.mch.weixin.qq.com";
+
+
+    /**
+     * Native下单
+     */
+    public static final String NATIVE_ORDER = HOST+ "/v3/pay/transactions/native";
+
+
+
+    /**
+     * Native订单状态查询, 根据商户订单号查询
+     */
+    public static final String NATIVE_QUERY = HOST+ "/v3/pay/transactions/out-trade-no/%s?mchid=%s";
+
+
+    /**
+     * 关闭订单接口
+     */
+    public static final String NATIVE_CLOSE_ORDER = HOST+ "/v3/pay/transactions/out-trade-no/%s/close";
+
+
+
+    /**
+     * 申请退款接口
+     */
+    public static final String NATIVE_REFUND_ORDER = HOST+ "/v3/refund/domestic/refunds";
+    /**
+     * 退款状态查询接口
+     */
+    public static final String NATIVE_REFUND_QUERY = HOST+ "/v3/refund/domestic/refunds/%s";
+}

+ 43 - 0
src/main/java/com/travel/wxPay/WxPayConfigure.java

@@ -0,0 +1,43 @@
+package com.travel.wxPay;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+public class WxPayConfigure {
+
+    /**
+     * 商户号
+     */
+    @Value("${pay.wechat.mchid}")
+    private String mchId;
+
+    /**
+     * 公众号id 需要和商户号绑定
+     */
+    @Value("${pay.wechat.appid}")
+    private String wxPayAppid;
+    /**
+     * 商户证书序列号,需要和证书对应
+     */
+    @Value("${pay.wechat.mchSerialNo}")
+    private String mchSerialNo;
+    /**
+     * API V3密钥
+     */
+    @Value("${pay.wechat.apiV3Key}")
+    private String apiV3Key;
+    /**
+     * 商户私钥路径(微信服务端会根据证书序列号,找到证书获取公钥进行解密数据)
+     */
+    @Value("${pay.wechat.privateKeyPath}")
+    private String privateKeyPath;
+    /**
+     * 支付成功,回调通知
+     */
+    @Value("${pay.wechat.callbackUrl}")
+    private String callbackUrl;
+}

+ 22 - 1
src/main/resources/application.yml

@@ -20,6 +20,13 @@ spring:
   mvc:
     async:
       request-timeout: 20000
+  redis:
+    host: 127.0.0.1
+    port: 6379
+    password:
+    jedis:
+      pool:
+        max-active: 0
 logging:
   level:
     com:
@@ -38,4 +45,18 @@ mybatis:
   mapperLocations: classpath:config/mapping/*.xml
   configuration:
     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-    map-underscore-to-camel-case: true
+    map-underscore-to-camel-case: true
+pay:
+  wechat:
+    #商户号
+    mchid:
+    #公众号id 需要和商户号绑定
+    appid:
+    #商户证书序列号,需要和证书对应
+    mchSerialNo:
+    #api密钥
+    apiV3Key:
+    #商户私钥路径(微信服务端会根据证书序列号,找到证书获取公钥进行解密数据)
+    privateKeyPath: classpath:/cert/apiclient_key.pem
+    #支付成功,回调通知
+    callbackUrl: