Browse Source

接口加密

zhangjiansheng 2 months ago
parent
commit
a52eadcef9

+ 4 - 1
eitc-common/pom.xml

@@ -181,7 +181,10 @@
181 181
             <groupId>cn.hutool</groupId>
182 182
             <artifactId>hutool-all</artifactId>
183 183
         </dependency>
184
-
184
+        <dependency>
185
+            <groupId>org.springframework</groupId>
186
+            <artifactId>spring-webmvc</artifactId>
187
+        </dependency>
185 188
     </dependencies>
186 189
 
187 190
 </project>

+ 118 - 0
eitc-common/src/main/java/com/eitc/common/config/ParameterRequestWrapper.java

@@ -0,0 +1,118 @@
1
+package com.eitc.common.config;
2
+
3
+import org.springframework.util.StreamUtils;
4
+
5
+import javax.servlet.ReadListener;
6
+import javax.servlet.ServletInputStream;
7
+import javax.servlet.http.HttpServletRequest;
8
+import javax.servlet.http.HttpServletRequestWrapper;
9
+import java.io.BufferedReader;
10
+import java.io.ByteArrayInputStream;
11
+import java.io.IOException;
12
+import java.io.InputStream;
13
+import java.io.InputStreamReader;
14
+import java.util.HashMap;
15
+import java.util.Map;
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+public class ParameterRequestWrapper  extends HttpServletRequestWrapper {
27
+    private Map<String , String[]> params = new HashMap<String, String[]>();
28
+    /**
29
+     * 缓存下来的HTTP body
30
+     */
31
+    private byte[] body;
32
+
33
+
34
+    @SuppressWarnings("unchecked")
35
+    public ParameterRequestWrapper(HttpServletRequest request) throws IOException {
36
+        // 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似
37
+        super(request);
38
+        //将参数表,赋予给当前的Map以便于持有request中的参数
39
+        this.params.putAll(request.getParameterMap());
40
+        //将参数表,赋予给当前的Map以便于持有request中的参数
41
+        this.body = StreamUtils.copyToByteArray(request.getInputStream());
42
+    }
43
+    //重载一个构造方法
44
+    public ParameterRequestWrapper(HttpServletRequest request , Map<String , Object> extendParams) throws IOException {
45
+        this(request);
46
+        addAllParameters(extendParams);//这里将扩展参数写入参数表
47
+    }
48
+
49
+
50
+    public void addAllParameters(Map<String , Object>otherParams) {//增加多个参数
51
+        for(Map.Entry<String , Object>entry : otherParams.entrySet()) {
52
+            addParameter(entry.getKey() , entry.getValue());
53
+        }
54
+    }
55
+
56
+
57
+    public void addParameter(String name , Object value) {//增加参数
58
+        if(value != null) {
59
+            if(value instanceof String[]) {
60
+                this.params.put(name , (String[])value);
61
+            }else if(value instanceof String) {
62
+                this.params.put(name , new String[] {(String)value});
63
+            }else {
64
+                this.params.put(name , new String[] {String.valueOf(value)});
65
+            }
66
+        }
67
+    }
68
+
69
+
70
+
71
+
72
+    /**
73
+     * 重新包装输入流
74
+     * @return
75
+     * @throws IOException
76
+     */
77
+    @Override
78
+    public ServletInputStream getInputStream() throws IOException {
79
+        InputStream bodyStream = new ByteArrayInputStream(body);
80
+        return new ServletInputStream() {
81
+
82
+            @Override
83
+            public int read() throws IOException {
84
+                return bodyStream.read();
85
+            }
86
+
87
+            /**
88
+             * 下面的方法一般情况下不会被使用,如果你引入了一些需要使用ServletInputStream的外部组件,可以重点关注一下。
89
+             * @return
90
+             */
91
+            @Override
92
+            public boolean isFinished() {
93
+                return false;
94
+            }
95
+
96
+            @Override
97
+            public boolean isReady() {
98
+                return true;
99
+            }
100
+
101
+            @Override
102
+            public void setReadListener(ReadListener readListener) {
103
+
104
+            }
105
+        };
106
+    }
107
+
108
+    @Override
109
+    public BufferedReader getReader() throws IOException {
110
+        return new BufferedReader(new InputStreamReader(getInputStream()));
111
+    }
112
+
113
+    public void setInputStream( byte[] in) {
114
+        this.body = in;
115
+    }
116
+
117
+
118
+}

+ 21 - 0
eitc-common/src/main/java/com/eitc/common/constant/HttpConst.java

@@ -0,0 +1,21 @@
1
+package com.eitc.common.constant;
2
+
3
+public class HttpConst {
4
+    /**
5
+     * 几种常见的Content-Type
6
+     */
7
+    public static final String FORM_URLENCODED_CONTENT_TYPE ="application/x-www-form-urlencoded";
8
+
9
+    public static final String JSON_CONTENT_TYPE = "application/json";
10
+
11
+    public static final String MULTIPART_CONTENT_TYPE = "multipart/form-data";
12
+    /**
13
+     * 常见的post/get请求方式
14
+     */
15
+    public static final String POST_METHOD = "post";
16
+
17
+    public static final String GET_METHOD = "GET";
18
+
19
+    public static final String OPTIONS_METHOD = "options";
20
+
21
+}

+ 4 - 1
eitc-common/src/main/java/com/eitc/common/core/domain/AjaxResult.java

@@ -2,7 +2,10 @@ package com.eitc.common.core.domain;
2 2
 
3 3
 import java.util.HashMap;
4 4
 import java.util.Objects;
5
+
6
+import com.alibaba.fastjson2.JSON;
5 7
 import com.eitc.common.constant.HttpStatus;
8
+import com.eitc.common.utils.CryptoUtil;
6 9
 import com.eitc.common.utils.StringUtils;
7 10
 
8 11
 /**
@@ -55,7 +58,7 @@ public class AjaxResult extends HashMap<String, Object>
55 58
         super.put(MSG_TAG, msg);
56 59
         if (StringUtils.isNotNull(data))
57 60
         {
58
-            super.put(DATA_TAG, data);
61
+            super.put(DATA_TAG,  data);
59 62
         }
60 63
     }
61 64
 

+ 60 - 0
eitc-common/src/main/java/com/eitc/common/filter/CustomResponseBodyAdviceAdapter.java

@@ -0,0 +1,60 @@
1
+package com.eitc.common.filter;
2
+
3
+import com.alibaba.fastjson2.JSON;
4
+import com.eitc.common.core.domain.AjaxResult;
5
+import com.eitc.common.core.page.TableDataInfo;
6
+import com.eitc.common.utils.CryptoUtil;
7
+import org.springframework.core.MethodParameter;
8
+import org.springframework.http.MediaType;
9
+import org.springframework.http.ResponseEntity;
10
+import org.springframework.http.server.ServerHttpRequest;
11
+import org.springframework.http.server.ServerHttpResponse;
12
+import org.springframework.web.bind.annotation.ControllerAdvice;
13
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
14
+
15
+import java.util.Map;
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+@ControllerAdvice
27
+public class CustomResponseBodyAdviceAdapter implements ResponseBodyAdvice<Object> {
28
+
29
+    @Override
30
+    public boolean supports(MethodParameter returnType, Class converterType) {
31
+        // 这里可以根据需要判断是否需要拦截,例如只拦截特定的Controller的方法
32
+        return true;
33
+    }
34
+
35
+    @Override
36
+    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
37
+                                  Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
38
+        // 这里可以对body进行处理,比如添加公共字段,或者修改返回格式等
39
+        if (body instanceof String) {
40
+            return "Custom: " + body;
41
+        } else if (body instanceof ResponseEntity) {
42
+            ResponseEntity<?> responseEntity = (ResponseEntity<?>) body;
43
+            Object originalBody = responseEntity.getBody();
44
+            return ResponseEntity.ok("Custom: " + originalBody);
45
+        }else if (body instanceof AjaxResult) {
46
+            AjaxResult ajaxResult = (AjaxResult) body;
47
+//            String encrypt = CryptoUtil.encrypt(JSON.toJSONString(ajaxResult));
48
+//            AjaxResult ajaxResult2 = new AjaxResult(200, "success", encrypt);
49
+//            return ajaxResult2;
50
+            return ajaxResult;
51
+        }else if (body instanceof TableDataInfo){
52
+            TableDataInfo tableDataInfo = (TableDataInfo) body;
53
+//            AjaxResult ajaxResult = new AjaxResult(200, "success", CryptoUtil.encrypt(JSON.toJSONString(tableDataInfo)));
54
+//            return ajaxResult;
55
+            return tableDataInfo;
56
+        }
57
+        System.out.println(body);
58
+        return body;
59
+    }
60
+}

+ 178 - 20
eitc-common/src/main/java/com/eitc/common/filter/RepeatableFilter.java

@@ -1,6 +1,14 @@
1 1
 package com.eitc.common.filter;
2 2
 
3
-import java.io.IOException;
3
+import com.alibaba.fastjson2.JSON;
4
+import com.alibaba.fastjson2.JSONObject;
5
+import com.alibaba.fastjson2.TypeReference;
6
+import com.eitc.common.config.ParameterRequestWrapper;
7
+import com.eitc.common.constant.HttpConst;
8
+import com.eitc.common.utils.CryptoUtil;
9
+import com.eitc.common.utils.StringUtils;
10
+import org.springframework.http.MediaType;
11
+
4 12
 import javax.servlet.Filter;
5 13
 import javax.servlet.FilterChain;
6 14
 import javax.servlet.FilterConfig;
@@ -8,45 +16,195 @@ import javax.servlet.ServletException;
8 16
 import javax.servlet.ServletRequest;
9 17
 import javax.servlet.ServletResponse;
10 18
 import javax.servlet.http.HttpServletRequest;
11
-import org.springframework.http.MediaType;
12
-import com.eitc.common.utils.StringUtils;
19
+import javax.servlet.http.HttpServletResponse;
20
+import java.io.ByteArrayOutputStream;
21
+import java.io.IOException;
22
+import java.nio.charset.StandardCharsets;
23
+import java.util.Arrays;
24
+import java.util.HashMap;
25
+import java.util.Map;
26
+import java.util.stream.Collectors;
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
13 36
 
14 37
 /**
15 38
  * Repeatable 过滤器
16 39
  *
17 40
  * @author eitc
18 41
  */
19
-public class RepeatableFilter implements Filter
20
-{
42
+public class RepeatableFilter implements Filter {
43
+
44
+    private static final String DECRYPT_PARAM_NAME = "decrypt";//请求参数包含的是否加密的字段
45
+
46
+    private static final String DEFAULT_DECRYPT_TYPE = "AES";//默认加密的类型
47
+
21 48
     @Override
22
-    public void init(FilterConfig filterConfig) throws ServletException
23
-    {
49
+    public void init(FilterConfig filterConfig) throws ServletException {
24 50
 
25 51
     }
26 52
 
27 53
     @Override
28 54
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
29
-            throws IOException, ServletException
30
-    {
31
-        ServletRequest requestWrapper = null;
32
-        if (request instanceof HttpServletRequest
33
-                && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE))
55
+            throws IOException, ServletException {
56
+        HttpServletRequest req = (HttpServletRequest) request;
57
+        HttpServletResponse res = (HttpServletResponse) response;
34 58
         {
59
+
60
+            String contentType = req.getContentType();//获取contentType请求头
61
+            String method = req.getMethod();//获取请求方法  post/get
62
+            //2 处理post请求  只处理application/x-www-form-urlencoded  application/json,对于multipart/form-data,直接放行
63
+            if (!method.trim().equalsIgnoreCase(HttpConst.GET_METHOD)) {
64
+                if (contentType.trim().toLowerCase().contains(HttpConst.MULTIPART_CONTENT_TYPE)) {
65
+                    chain.doFilter(req, response);
66
+                    return;
67
+                }
68
+                //处理application/x-www-form-urlencoded
69
+                if (contentType.trim().toLowerCase().contains(HttpConst.FORM_URLENCODED_CONTENT_TYPE)) {
70
+                    String decrypt = request.getParameter(DECRYPT_PARAM_NAME);
71
+                    if(decrypt==null||decrypt.trim().isEmpty()){
72
+                        chain.doFilter(request, response);
73
+                        return;
74
+                    }
75
+                    chain.doFilter(req, response);
76
+                    return;
77
+                }
78
+                //处理application/json
79
+                if (contentType.trim().toLowerCase().contains(HttpConst.JSON_CONTENT_TYPE)) {
80
+                    ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper(req);
81
+//                    String collect = requestWrapper.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
82
+//                    // 解密
83
+//                    String decrypt = CryptoUtil.decrypt(collect);
84
+//                    System.out.println(decrypt);
85
+//                    byte[] bytes = decrypt.getBytes(StandardCharsets.UTF_8);
86
+//                    ByteArrayOutputStream binaryStream = new ByteArrayOutputStream();
87
+//                    try {
88
+//                        binaryStream.write(bytes);
89
+//                        byte[] byteArray = binaryStream.toByteArray();
90
+//                        requestWrapper.setInputStream(byteArray);
91
+//                    } catch (IOException e) {
92
+//                        e.printStackTrace();
93
+//                    } finally {
94
+//                        try {
95
+//                            binaryStream.close();
96
+//                        } catch (IOException e) {
97
+//                            e.printStackTrace();
98
+//                        }
99
+//                    }
100
+                    chain.doFilter(requestWrapper, response);
101
+                    return;
102
+                }
103
+            }
104
+        }
105
+
106
+
107
+        HttpServletRequest requestWrapper = null;
108
+        if (req instanceof HttpServletRequest
109
+                && StringUtils.startsWithIgnoreCase(req.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
35 110
             requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response);
36 111
         }
37
-        if (null == requestWrapper)
38
-        {
39
-            chain.doFilter(request, response);
112
+        if (null == requestWrapper) {
113
+            chain.doFilter(req, res);
114
+        } else {
115
+            chain.doFilter(requestWrapper, res);
40 116
         }
41
-        else
42
-        {
43
-            chain.doFilter(requestWrapper, response);
117
+    }
118
+
119
+
120
+    /**
121
+     * 字符串解密
122
+     *
123
+     * @param value
124
+     * @return
125
+     */
126
+    public String decryptParam(String value, String decryptType) {
127
+        if (decryptType.trim().equalsIgnoreCase(DEFAULT_DECRYPT_TYPE)) {
128
+//            return AESCipher.decryptAES(value,AESCipher.KEY);
129
+            return "111";
130
+        } else {
131
+            return value + "+++";
132
+        }
133
+
134
+    }
135
+
136
+    /**
137
+     * 普通的post/get请求
138
+     *
139
+     * @param parameterMap
140
+     */
141
+    public Map<String, String[]> decryptParamForNormalRequest(Map<String, String[]> parameterMap, String decryptType) {
142
+        Map<String, String[]> decryptMap = new HashMap<>();
143
+        if (parameterMap == null || parameterMap.size() == 0) {
144
+            return decryptMap;
145
+        }
146
+        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
147
+            String key = entry.getKey();
148
+            if (key == null || key.trim().equalsIgnoreCase(DECRYPT_PARAM_NAME)) {
149
+                continue;
150
+            }
151
+            String[] value = entry.getValue();
152
+            //String decryptKey = decodeParam(key,decryptType);
153
+            String[] decryptValue = null;
154
+            if (value != null && value.length > 0) {
155
+                decryptValue = new String[value.length];
156
+                for (int i = 0; i < value.length; i++) {
157
+                    decryptValue[i] = decryptParam(value[i], decryptType);
158
+                }
159
+            }
160
+            decryptMap.put(key, decryptValue);
161
+        }
162
+        //打印用
163
+        StringBuffer printStr = new StringBuffer();
164
+        for (Map.Entry<String, String[]> entry1 : decryptMap.entrySet()) {
165
+            printStr.append(entry1.getKey()).append("=").append(Arrays.asList(entry1.getValue())).append("&");
166
+        }
167
+        System.out.println("ParamsFilter:发送的请求参数:" + JSON.toJSONString(printStr));
168
+        return decryptMap;
169
+    }
170
+
171
+    /**
172
+     * post的application/json请求
173
+     *
174
+     * @param body
175
+     */
176
+    public String decryptParamForPostJsonRequest(String body, String decryptType) {
177
+        String decryptBody = "{}";
178
+        if (body == null || body.trim().isEmpty() || body.trim().equalsIgnoreCase("{}") || !body.trim().contains(":")) {
179
+            return decryptBody;
180
+        }
181
+        Map<String, Object> map = JSON.parseObject(body, new TypeReference<Map<String, Object>>() {
182
+        });
183
+        if (map == null || map.size() == 0) {
184
+            return decryptBody;
185
+        }
186
+        Map<String, Object> decryptMap = new HashMap<>();
187
+        for (Map.Entry<String, Object> entry : map.entrySet()) {
188
+            String key = entry.getKey();
189
+            if (key == null || key.trim().equalsIgnoreCase(DECRYPT_PARAM_NAME)) {
190
+                continue;
191
+            }
192
+            Object value = entry.getValue();
193
+            String valueStr = String.valueOf(value);
194
+            if (valueStr == null || valueStr.trim().isEmpty() || valueStr.trim().equalsIgnoreCase("null")) {
195
+                valueStr = null;
196
+            }
197
+            decryptMap.put(key, decryptParam(valueStr, decryptType));
44 198
         }
199
+        decryptBody = JSON.toJSONString(decryptMap);
200
+        System.out.println("ParamsFilter:发送的请求参数:" + decryptBody);
201
+        return decryptBody;
45 202
     }
46 203
 
47 204
     @Override
48
-    public void destroy()
49
-    {
205
+    public void destroy() {
50 206
 
51 207
     }
208
+
209
+
52 210
 }

+ 116 - 0
eitc-common/src/main/java/com/eitc/common/utils/CryptoUtil.java

@@ -0,0 +1,116 @@
1
+package com.eitc.common.utils;
2
+
3
+import org.apache.commons.codec.binary.Base64;
4
+
5
+import javax.crypto.Cipher;
6
+import javax.crypto.spec.IvParameterSpec;
7
+import javax.crypto.spec.SecretKeySpec;
8
+import java.nio.charset.StandardCharsets;
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+public class CryptoUtil {
20
+
21
+    private final static String IV = "1234567890123456";//需要前端与后端配置一致
22
+    private final static String KEY = "1234567890123456";
23
+
24
+    /***
25
+     * 加密
26
+     * @param  data 要加密的数据
27
+     * @return encrypt
28
+     */
29
+    public static String encrypt(String data){
30
+        return encrypt(data, KEY, IV);
31
+    }
32
+
33
+    public static Object decryptObject(Object obj) throws Exception{
34
+        return ObjectUtils.decryptObjectValues(obj);
35
+    }
36
+
37
+    /***
38
+     * param data 需要解密的数据
39
+     * 调用desEncrypt()方法
40
+     */
41
+    public static String decrypt(String data){
42
+        return decrypt(data, KEY, IV);
43
+    }
44
+
45
+    /**
46
+     * 加密方法
47
+     * @param data  要加密的数据
48
+     * @param key 加密key
49
+     * @param iv 加密iv
50
+     * @return 加密的结果
51
+     */
52
+    private static String encrypt(String data, String key, String iv){
53
+        try {
54
+            //"算法/模式/补码方式"NoPadding PkcsPadding
55
+            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
56
+            int blockSize = cipher.getBlockSize();
57
+
58
+            byte[] dataBytes = data.getBytes();
59
+            int plaintextLength = dataBytes.length;
60
+            if (plaintextLength % blockSize != 0) {
61
+                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
62
+            }
63
+
64
+            byte[] plaintext = new byte[plaintextLength];
65
+            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
66
+
67
+            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
68
+            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
69
+
70
+            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
71
+            byte[] encrypted = cipher.doFinal(plaintext);
72
+
73
+            return Base64.encodeBase64String(encrypted);
74
+
75
+        } catch (Exception e) {
76
+            e.printStackTrace();
77
+            return null;
78
+        }
79
+    }
80
+
81
+    /**
82
+     * 解密方法
83
+     * @param data 要解密的数据
84
+     * @param key  解密key
85
+     * @param iv 解密iv
86
+     * @return 解密的结果
87
+     */
88
+    private static String decrypt(String data, String key, String iv){
89
+        try {
90
+            if(StringUtils.isEmpty(data)) {
91
+                return "";
92
+            }
93
+            byte[] encrypted1 = new Base64().decode(data);
94
+            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
95
+            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
96
+            IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
97
+            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
98
+            byte[] original = cipher.doFinal(encrypted1);
99
+            return new String(original, StandardCharsets.UTF_8).trim();
100
+
101
+        } catch (Exception e) {
102
+            e.printStackTrace();
103
+            return "解析错误";
104
+        }
105
+    }
106
+
107
+    public static void main(String[] args) {
108
+
109
+
110
+
111
+       String a = encrypt("123456");
112
+       System.out.println(a);
113
+       String b = decrypt(a);
114
+       System.out.println(b);
115
+    }
116
+}

+ 50 - 0
eitc-common/src/main/java/com/eitc/common/utils/ObjectUtils.java

@@ -0,0 +1,50 @@
1
+package com.eitc.common.utils;
2
+
3
+import com.eitc.common.utils.bean.BeanUtils;
4
+import lombok.extern.log4j.Log4j2;
5
+
6
+import java.beans.PropertyDescriptor;
7
+import java.lang.reflect.InvocationTargetException;
8
+import java.lang.reflect.Method;
9
+
10
+import static sun.invoke.util.Wrapper.isPrimitiveType;
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+@Log4j2
21
+public class ObjectUtils {
22
+    public static Object decryptObjectValues(Object obj) throws Exception{
23
+        if (obj == null) {
24
+            return null;
25
+        }
26
+        Class<?> objClass = obj.getClass();
27
+        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(objClass);
28
+        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
29
+            Method readMethod = propertyDescriptor.getReadMethod();
30
+            Method writeMethod = propertyDescriptor.getWriteMethod();
31
+            if (readMethod != null && writeMethod!=null) {
32
+                try {
33
+                    Object value = readMethod.invoke(obj);
34
+                    if(value==null) {
35
+                        continue;
36
+                    }
37
+                    if(isPrimitiveType(value.getClass()) && value instanceof String) {
38
+                        String changeValue = CryptoUtil.decrypt((String)value);
39
+                        writeMethod.invoke(obj, changeValue);
40
+                    }
41
+                } catch (IllegalAccessException | InvocationTargetException e) {
42
+                    e.printStackTrace();
43
+                    log.error("反射获取类【" + objClass.getName() + "】方法异常,", e);
44
+                }
45
+            }
46
+        }
47
+        return obj;
48
+
49
+    }
50
+}