Browse Source

进参自动解密,出参自动加密

liu 3 years ago
parent
commit
e377517589

+ 15 - 0
pom.xml

@@ -89,6 +89,21 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.67</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.6</version>
+        </dependency>
 
     </dependencies>
 

+ 16 - 0
src/main/java/com/example/faceverification/annotation/Encrypt.java

@@ -0,0 +1,16 @@
+package com.example.faceverification.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ *  加密注解
+ *
+ * @author wang
+ * @date 2022-05-20
+ * */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Encrypt {
+
+}

+ 72 - 0
src/main/java/com/example/faceverification/aop/EncryptAOP.java

@@ -0,0 +1,72 @@
+package com.example.faceverification.aop;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.api.R;
+import com.example.faceverification.annotation.Encrypt;
+import com.example.faceverification.core.RequestWrapper;
+import com.example.faceverification.utils.AesUtils;
+import com.example.faceverification.utils.ServletUtil;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.bouncycastle.jcajce.provider.symmetric.AES;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ *  传输加密模块AOP,对接口的出参进行加密
+ *  注意顺序不能乱,此AOP必须第一个执行,因为
+ *  最先执行的最后结束,这样才能在各个AOP都执行
+ *  完毕之后完成最后的加密
+ *
+ * @author
+ * @date
+ * */
+@Order(value = 0)
+@Aspect
+@Component
+public class EncryptAOP {
+
+
+
+    @Pointcut("@annotation(com.example.faceverification.annotation.Encrypt)")
+    public void point () {}
+
+
+    /**
+     *  环绕增强,加密出参
+     * */
+    @Around(value = "point() && @annotation(encrypt)")
+    public Object around (ProceedingJoinPoint joinPoint , Encrypt encrypt) throws Throwable {
+        //返回的结果
+        Object returnValue = null;
+        //从当前上下文提取request
+        HttpServletRequest request = ServletUtil.getRequest();
+        //请求体中的json参数
+        String body = "";
+
+        //把可能出错的逻辑,放到try-catch
+        try {
+            body = new RequestWrapper(request).getBodyString();
+        }catch (Exception e) {
+
+        }finally {
+            //不管怎样,保证业务必须执行
+            returnValue = joinPoint.proceed();
+        }
+
+        //把接口的出参,用前端此次传过来的AES密钥进行加密,然后返回
+//        JSONObject jsonObject = JSON.parseObject(body);
+        String encryptData = AesUtils.encrypt(returnValue.toString() , "6edfcc178c0f415d8e6628238761976f");
+
+        returnValue=encryptData;
+        return returnValue;
+    }
+
+
+}

+ 4 - 3
src/main/java/com/example/faceverification/api/IdentityComparisonRecordControllerAPI.java

@@ -1,12 +1,13 @@
 package com.example.faceverification.api;
 
 import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 
-@RequestMapping("/api/identity-comparison-record")
+
 public interface IdentityComparisonRecordControllerAPI {
 
-    @PostMapping("/test")
-    void test(String name,String value);
+
+    String test(String name);
 
 }

+ 37 - 0
src/main/java/com/example/faceverification/config/MySecurity.java

@@ -0,0 +1,37 @@
+package com.example.faceverification.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+//@ConfigurationProperties(prefix = "my-security")
+public class MySecurity {
+    @Value("${my-security.privateKey}")
+    public String privateKey;
+    @Value("${my-security.publicKey}")
+    public String publicKey;
+
+    public String getPrivateKey() {
+        return privateKey;
+    }
+
+    public String getPublicKey() {
+        return publicKey;
+    }
+
+    @Override
+    public String toString() {
+        return "MySecurity{" +
+                "privateKey='" + privateKey + '\'' +
+                ", publicKey='" + publicKey + '\'' +
+                '}';
+    }
+
+    public void setPrivateKey(String privateKey) {
+        this.privateKey = privateKey;
+    }
+
+    public void setPublicKey(String publicKey) {
+        this.publicKey = publicKey;
+    }
+}

+ 10 - 2
src/main/java/com/example/faceverification/controller/IdentityComparisonRecordController.java

@@ -1,7 +1,10 @@
 package com.example.faceverification.controller;
 
 
+import com.example.faceverification.annotation.Encrypt;
 import com.example.faceverification.api.IdentityComparisonRecordControllerAPI;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 import org.springframework.web.bind.annotation.RestController;
@@ -15,12 +18,17 @@ import org.springframework.web.bind.annotation.RestController;
  * @since 2023-04-27
  */
 @RestController
+@RequestMapping("/api/identity-comparison-record")
 public class IdentityComparisonRecordController implements IdentityComparisonRecordControllerAPI {
 
+
     @Override
-    public void test(String name, String value) {
+    @PostMapping("/test")
+    @Encrypt
+    public String test(@RequestBody String name) {
         System.out.println("name = " + name);
-        System.out.println("value = " + value);
+        System.out.println("测试");
+        return "123456";
     }
 }
 

+ 46 - 15
src/main/java/com/example/faceverification/core/JwtAuthenticationInterceptor.java

@@ -1,15 +1,18 @@
 package com.example.faceverification.core;
 
-import org.springframework.boot.configurationprocessor.json.JSONObject;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
 import org.springframework.util.StringUtils;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.ModelAndView;
 
-import javax.servlet.ServletInputStream;
+import javax.servlet.*;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.reflect.Method;
 import java.util.Collections;
@@ -38,27 +41,55 @@ public class JwtAuthenticationInterceptor implements HandlerInterceptor {
      */
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        HashMap<Object, Object> map = new HashMap<>();
+//        HashMap<String, Object> map = new HashMap<>();
+//
+////       获取所有参数的名字
+//        Enumeration<String> parameterNames = request.getParameterNames();
+////      获取所有参数对应的Map,其中key为参数名,value为参数值
+//        Map<String, String[]> parameterMap = request.getParameterMap();
+////通过Enumeration的hasMoreElements()方法遍历.再由nextElement()方法获得枚举的值
+//        while(parameterNames.hasMoreElements()){
+//            String name=parameterNames.nextElement();
+//            String value=request.getParameter(name);
+//
+//
+//        }
+//        String ss = request.getParameter("name");
+//        RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest) request);
+//        //GET请求不加密
+//        if (requestWrapper.getMethod().equals("GET")) {
+//            return true;
+//        }
+//        //读出json请求体
+//        StringBuffer buffer = new StringBuffer();
+//        String line = null;
+//        BufferedReader reader = null;
+//        reader = requestWrapper.getReader();
+//        while ((line = reader.readLine()) != null) {
+//            buffer.append(line);
+//        }
+//        //解密
+//        JSONObject jsonObject = JSON.parseObject(buffer.toString());
+////        String name = jsonObject.getString("name");
+//       String name="已修改";
 
-//       获取所有参数的名字
-        Enumeration<String> parameterNames = request.getParameterNames();
-//      获取所有参数对应的Map,其中key为参数名,value为参数值
-        Map<String, String[]> parameterMap = request.getParameterMap();
-//通过Enumeration的hasMoreElements()方法遍历.再由nextElement()方法获得枚举的值
-        while(parameterNames.hasMoreElements()){
-            String name=parameterNames.nextElement();
-            String value=request.getParameter(name);
-            map.put(name,"666666");
-        }
-        Collections.unmodifiableMap(map);
 
+        //重置json请求体,保证下游业务无感知获取数据
+//        requestWrapper.setBody(name.getBytes());
 
         return true;
     }
 
     @Override
     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
-
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("application/json; charset=utf-8");
+        PrintWriter out = null;
+        JSONObject res = new JSONObject();
+        res.put("success", "false");
+        res.put("msg", "xxxx");
+        out = response.getWriter();
+        out.append(res.toString());
     }
 
     /**

+ 15 - 10
src/main/java/com/example/faceverification/core/JwtlnterceptorConfig.java

@@ -1,25 +1,30 @@
 package com.example.faceverification.core;
 
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
+import javax.servlet.Filter;
+
 @Configuration
 public class JwtlnterceptorConfig implements WebMvcConfigurer {
-    @Override
-    public void addInterceptors(InterceptorRegistry registry) {
-        //region
-        //目前测试下来 使用 /**所有的话,response.sendError浏览器获取不到响应的信息
-        //默认拦截所有路径
-        registry.addInterceptor(authenticationInterceptor())
-                .addPathPatterns("/api/**");
-        //endregion
+    /**
+     * 注册过滤器
+     */
+    @Bean
+    public FilterRegistrationBean someFilterRegistration() {
+        FilterRegistrationBean registration = new FilterRegistrationBean();
+        registration.setFilter(replaceStreamFilter());
+        registration.addUrlPatterns("/*");
+//        registration.setName("streamFilter");
+        return registration;
     }
 
     @Bean
-    public HandlerInterceptor authenticationInterceptor() {
-        return new JwtAuthenticationInterceptor();
+    public Filter replaceStreamFilter() {
+        return new ReplaceStreamFilter();
     }
 }

+ 73 - 0
src/main/java/com/example/faceverification/core/ReplaceStreamFilter.java

@@ -0,0 +1,73 @@
+package com.example.faceverification.core;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.example.faceverification.utils.AesUtils;
+import com.example.faceverification.utils.RSAUtils;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
+import java.io.IOException;
+
+/**
+ *  替换请求体的过滤逻辑
+ *
+ * @author wang
+ * @since 2022-05-20
+ **/
+@Slf4j
+public class ReplaceStreamFilter implements Filter {
+    @Value("${my-security.privateKey}")
+    public String privateKey;
+ 
+    @SneakyThrows
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        //转换自己的wrapper,实现多次读写
+        RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest) request);
+
+        //GET请求不加密
+        if (requestWrapper.getMethod().equals("GET")) {
+            chain.doFilter(requestWrapper, response);
+            return;
+        }
+
+
+
+        //读出json请求体
+        StringBuffer buffer = new StringBuffer();
+        String line = null;
+        BufferedReader reader = null;
+        reader = requestWrapper.getReader();
+        while ((line = reader.readLine()) != null) {
+            buffer.append(line);
+        }
+
+        //解密
+        JSONObject jsonObject = JSON.parseObject(buffer.toString());
+        //得到的密文
+        String data = jsonObject.getString("data");
+
+        String decrypt = RSAUtils.decrypt(data, RSAUtils.getPrivateKey(privateKey));
+        //再用AES的key解密数据
+        AesUtils.decrypt(decrypt,"");
+
+        //把解密之后的aesKey一并交给下游,方便在AOP对出参加密
+//        jsonObject = JSON.parseObject(data);
+//        jsonObject.put("aesKey" , aesKey);
+
+        //重置json请求体,保证下游业务无感知获取数据
+        requestWrapper.setBody(jsonObject.toJSONString().getBytes());
+       
+        chain.doFilter(requestWrapper, response);
+    }
+ 
+    @Override
+    public void destroy() {
+
+    }
+}

+ 1 - 1
src/main/java/com/example/faceverification/common/RequestWrapper.java

@@ -1,4 +1,4 @@
-package com.example.faceverification.common;
+package com.example.faceverification.core;
  
 import lombok.extern.slf4j.Slf4j;
  

+ 209 - 0
src/main/java/com/example/faceverification/utils/AesUtils.java

@@ -0,0 +1,209 @@
+package com.example.faceverification.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.bouncycastle.util.encoders.Base64;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @Author: liujun
+ * @Description: Aes 加解密算法
+ * @Date Create in 上午 9:38$ 2017/12/26 0026$
+ * @Modify By:
+ */
+@Slf4j
+public class AesUtils {
+    /**
+     * @Author liujun
+     * @Description:
+     * @params: * @param content 需要加密的内容
+     * @param password  加密密码
+     * @Date 上午 9:41 2017/12/26 0026
+     */
+
+    public static String encrypt(String content, String password) {
+        //数据为空,不需要进行加解密,否则会出现空指针异常
+        if(StringUtils.isEmpty(content)) {
+            return null;
+        }
+
+        if(password.length()<16) {
+            password = password + "0000000000000000".substring(0, 16-password.length());
+        }
+        else if(password.length()>16) {
+            password = password.substring(0, 16);
+        }
+
+        return bytes2HexString(encryptAES(content.getBytes(), password.getBytes()));
+    }
+
+
+    /**
+     * @Author liujun
+     * @Description:
+     * @params: * @param content 待解密内容
+     * @param password 解密密钥
+     * @Date 上午 9:40 2017/12/26 0026
+     */
+    public static String decrypt(String content, String password) {
+        //数据为空,不需要进行加解密,否则会出现空指针异常
+        if(StringUtils.isEmpty(content)) {
+            return null;
+        }
+
+        if(password.length()<16) {
+            password = password + "0000000000000000".substring(0, 16-password.length());
+        }
+        else if(password.length()>16) {
+            password = password.substring(0, 16);
+        }
+
+        return new String(decryptAES(hexString2Bytes(content), password.getBytes()));
+    }
+
+    /**
+     * AES 加密
+     *
+     * @param data 明文
+     * @param key  16、24、32 字节秘钥
+     * @return 密文
+     */
+    public static byte[] encryptAES(final byte[] data,
+                                    final byte[] key) {
+
+        try {
+            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
+            Cipher cipher = Cipher.getInstance("AES");// 创建密码器
+            byte[] byteContent = data;
+            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);// 初始化
+            byte[] result = cipher.doFinal(byteContent);
+            return result; // 加密
+        } catch (NoSuchAlgorithmException e) {
+            log.error(e.getMessage(),e);
+        } catch (NoSuchPaddingException e) {
+            log.error(e.getMessage(),e);
+        } catch (InvalidKeyException e) {
+            log.error(e.getMessage(),e);
+        } catch (IllegalBlockSizeException e) {
+            log.error(e.getMessage(),e);
+        } catch (BadPaddingException e) {
+            log.error(e.getMessage(),e);
+        } catch (Exception e) {
+            log.error(e.getMessage(),e);
+        }
+        return null;
+    }
+
+    /**
+     * AES 解密
+     *
+     * @param data 密文
+     * @param key  16、24、32 字节秘钥
+     * @return 明文
+     */
+    public static byte[] decryptAES(final byte[] data,
+                                    final byte[] key) {
+
+        try {
+            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
+            Cipher cipher = Cipher.getInstance("AES");// 创建密码器
+            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);// 初始化
+            byte[] result = cipher.doFinal(data);
+            return result; // 加密
+        } catch (NoSuchAlgorithmException e) {
+            log.error(e.getMessage(),e);
+        } catch (NoSuchPaddingException e) {
+            log.error(e.getMessage(),e);
+        } catch (InvalidKeyException e) {
+            log.error(e.getMessage(),e);
+        } catch (IllegalBlockSizeException e) {
+            log.error(e.getMessage(),e);
+        } catch (BadPaddingException e) {
+            log.error(e.getMessage(),e);
+        } catch (Exception e) {
+            log.error(e.getMessage(),e);
+        }
+        return null;
+    }
+
+    public static String bytes2HexString(final byte[] bytes) {
+        if (bytes == null) return null;
+        int len = bytes.length;
+        if (len <= 0) return null;
+        char[] ret = new char[len << 1];
+        for (int i = 0, j = 0; i < len; i++) {
+            ret[j++] = hexDigits[bytes[i] >>> 4 & 0x0f];
+            ret[j++] = hexDigits[bytes[i] & 0x0f];
+        }
+        return new String(ret);
+    }
+
+    private static final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+    public static byte[] hexString2Bytes(String hexString) {
+        if (isSpace(hexString)) return null;
+        int len = hexString.length();
+        if (len % 2 != 0) {
+            hexString = "0" + hexString;
+            len = len + 1;
+        }
+        char[] hexBytes = hexString.toUpperCase().toCharArray();
+        byte[] ret = new byte[len >> 1];
+        for (int i = 0; i < len; i += 2) {
+            ret[i >> 1] = (byte) (hex2Dec(hexBytes[i]) << 4 | hex2Dec(hexBytes[i + 1]));
+        }
+        return ret;
+    }
+
+    private static int hex2Dec(final char hexChar) {
+        if (hexChar >= '0' && hexChar <= '9') {
+            return hexChar - '0';
+        } else if (hexChar >= 'A' && hexChar <= 'F') {
+            return hexChar - 'A' + 10;
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    public static byte[] base64Encode(final byte[] input) {
+        return Base64.encode(input);
+    }
+
+    public static byte[] base64Decode(final byte[] input) {
+
+        return Base64.decode(input);
+    }
+
+    private static boolean isSpace(final String s) {
+        if (s == null) return true;
+        for (int i = 0, len = s.length(); i < len; ++i) {
+            if (!Character.isWhitespace(s.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static void main(String[] args) {
+        String s="{\n" +
+                "  \"categoryId\": \"d0c248256f8346d2a19afa296562b319\",\n" +
+                "     \"luid\": \"80A036AE25AC\",\n" +
+                "     \"type\":\"5\",\n" +
+                "     \"userName\":\"13097286670\",\n" +
+                "\"startTime\":\"1682498606000\",\n" +
+                "     \"endTime\":\"1684981484000\"\n" +
+                "}";
+        //加密
+        String encryptString = AesUtils.encrypt(s, "6edfcc178c0f415d8e6628238761976f");
+        System.out.println("加密后字符串:"+encryptString);
+        //解密
+        String decryptString = AesUtils.decrypt(encryptString, "6edfcc178c0f415d8e6628238761976f");
+        System.out.println("解密后字符串:"+decryptString);
+    }
+}

File diff suppressed because it is too large
+ 195 - 0
src/main/java/com/example/faceverification/utils/RSAUtils.java


+ 55 - 0
src/main/java/com/example/faceverification/utils/ServletUtil.java

@@ -0,0 +1,55 @@
+package com.example.faceverification.utils;
+ 
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ *  Servlet工具类
+ *
+ * @author wang
+ * @since 2022-05-19
+ * */
+public class ServletUtil {
+
+    /** 返回一个HttpServletRequest
+     * @return request请求体
+     * */
+    public static HttpServletRequest getRequest(){
+        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        return requestAttributes.getRequest();
+    }
+
+    /** 返回一个HttpServletResponse
+     * @return response响应流
+     * */
+    public static HttpServletResponse getResponse(){
+        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
+    }
+
+    /**
+     * 获取请求Body
+     *
+     * @param request
+     * @return
+     */
+    public static JSONObject getBody(ServletRequest request) throws IOException {
+        BufferedReader streamReader = new BufferedReader( new InputStreamReader(request.getInputStream(), "UTF-8"));
+        StringBuilder responseStrBuilder = new StringBuilder();
+        String inputStr;
+        while ((inputStr = streamReader.readLine()) != null){
+            responseStrBuilder.append(inputStr);
+        }
+        JSONObject jsonObject = JSONObject.parseObject(responseStrBuilder.toString());
+        return jsonObject;
+    }
+
+}
+ 

File diff suppressed because it is too large
+ 5 - 0
src/main/resources/application.yml