xiongzhu 4 years ago
parent
commit
e4f8abd103

+ 87 - 69
src/main/java/cn/licoy/encryptbody/advice/EncryptResponseBodyAdvice.java

@@ -10,6 +10,7 @@ import cn.licoy.encryptbody.util.*;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import cn.licoy.encryptbody.config.EncryptBodyConfig;
+import jodd.util.ReflectUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.MethodParameter;
@@ -21,16 +22,21 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
 
 import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
 
 
 /**
  * 响应数据的加密处理<br>
- *     本类只对控制器参数中含有<strong>{@link org.springframework.web.bind.annotation.ResponseBody}</strong>
- *     或者控制类上含有<strong>{@link org.springframework.web.bind.annotation.RestController}</strong>
- *     以及package为<strong><code>cn.licoy.encryptbody.annotation.encrypt</code></strong>下的注解有效
- * @see ResponseBodyAdvice
+ * 本类只对控制器参数中含有<strong>{@link org.springframework.web.bind.annotation.ResponseBody}</strong>
+ * 或者控制类上含有<strong>{@link org.springframework.web.bind.annotation.RestController}</strong>
+ * 以及package为<strong><code>cn.licoy.encryptbody.annotation.encrypt</code></strong>下的注解有效
+ *
  * @author licoy.cn
  * @version 2018/9/4
+ * @see ResponseBodyAdvice
  */
 @Order(1)
 @ControllerAdvice
@@ -51,18 +57,28 @@ public class EncryptResponseBodyAdvice implements ResponseBodyAdvice {
     @Override
     public boolean supports(MethodParameter returnType, Class converterType) {
         Annotation[] annotations = returnType.getDeclaringClass().getAnnotations();
-        if(annotations!=null && annotations.length>0){
+        if (annotations != null && annotations.length > 0) {
             for (Annotation annotation : annotations) {
-                if(annotation instanceof EncryptBody ||
-                    annotation instanceof AESEncryptBody ||
-                    annotation instanceof DESEncryptBody ||
-                    annotation instanceof RSAEncryptBody ||
-                    annotation instanceof MD5EncryptBody ||
-                    annotation instanceof SHAEncryptBody){
+                if (annotation instanceof EncryptBody ||
+                        annotation instanceof AESEncryptBody ||
+                        annotation instanceof DESEncryptBody ||
+                        annotation instanceof RSAEncryptBody ||
+                        annotation instanceof MD5EncryptBody ||
+                        annotation instanceof SHAEncryptBody) {
                     return true;
                 }
             }
         }
+        if (Arrays.stream(ReflectUtil.getSuperclasses(returnType.getDeclaringClass()))
+                .flatMap(clazz -> Arrays.stream(clazz.getAnnotations()))
+                .anyMatch(annotation -> annotation instanceof EncryptBody ||
+                        annotation instanceof AESEncryptBody ||
+                        annotation instanceof DESEncryptBody ||
+                        annotation instanceof RSAEncryptBody ||
+                        annotation instanceof MD5EncryptBody ||
+                        annotation instanceof SHAEncryptBody)) {
+            return true;
+        }
         return returnType.getMethod().isAnnotationPresent(EncryptBody.class) ||
                 returnType.getMethod().isAnnotationPresent(AESEncryptBody.class) ||
                 returnType.getMethod().isAnnotationPresent(DESEncryptBody.class) ||
@@ -74,8 +90,8 @@ public class EncryptResponseBodyAdvice implements ResponseBodyAdvice {
     @Override
     public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                   Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
-        if(body==null) return null;
-        response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
+        if (body == null) return null;
+        response.getHeaders().add("Content-Encrypted", "true");
         String str = null;
         try {
             str = objectMapper.writeValueAsString(body);
@@ -83,11 +99,11 @@ public class EncryptResponseBodyAdvice implements ResponseBodyAdvice {
             e.printStackTrace();
         }
         EncryptAnnotationInfoBean classAnnotation = getClassAnnotation(returnType.getDeclaringClass());
-        if(classAnnotation!=null){
+        if (classAnnotation != null) {
             return switchEncrypt(str, classAnnotation);
         }
         EncryptAnnotationInfoBean methodAnnotation = getMethodAnnotation(returnType);
-        if(methodAnnotation!=null){
+        if (methodAnnotation != null) {
             return switchEncrypt(str, methodAnnotation);
         }
         throw new EncryptBodyFailException();
@@ -95,11 +111,12 @@ public class EncryptResponseBodyAdvice implements ResponseBodyAdvice {
 
     /**
      * 获取方法控制器上的加密注解信息
+     *
      * @param methodParameter 控制器方法
      * @return 加密注解信息
      */
-    private EncryptAnnotationInfoBean getMethodAnnotation(MethodParameter methodParameter){
-        if(methodParameter.getMethod().isAnnotationPresent(EncryptBody.class)){
+    private EncryptAnnotationInfoBean getMethodAnnotation(MethodParameter methodParameter) {
+        if (methodParameter.getMethod().isAnnotationPresent(EncryptBody.class)) {
             EncryptBody encryptBody = methodParameter.getMethodAnnotation(EncryptBody.class);
             return EncryptAnnotationInfoBean.builder()
                     .encryptBodyMethod(encryptBody.value())
@@ -107,24 +124,24 @@ public class EncryptResponseBodyAdvice implements ResponseBodyAdvice {
                     .shaEncryptType(encryptBody.shaType())
                     .build();
         }
-        if(methodParameter.getMethod().isAnnotationPresent(MD5EncryptBody.class)){
+        if (methodParameter.getMethod().isAnnotationPresent(MD5EncryptBody.class)) {
             return EncryptAnnotationInfoBean.builder()
                     .encryptBodyMethod(EncryptBodyMethod.MD5)
                     .build();
         }
-        if(methodParameter.getMethod().isAnnotationPresent(SHAEncryptBody.class)){
+        if (methodParameter.getMethod().isAnnotationPresent(SHAEncryptBody.class)) {
             return EncryptAnnotationInfoBean.builder()
                     .encryptBodyMethod(EncryptBodyMethod.SHA)
                     .shaEncryptType(methodParameter.getMethodAnnotation(SHAEncryptBody.class).value())
                     .build();
         }
-        if(methodParameter.getMethod().isAnnotationPresent(DESEncryptBody.class)){
+        if (methodParameter.getMethod().isAnnotationPresent(DESEncryptBody.class)) {
             return EncryptAnnotationInfoBean.builder()
                     .encryptBodyMethod(EncryptBodyMethod.DES)
                     .key(methodParameter.getMethodAnnotation(DESEncryptBody.class).otherKey())
                     .build();
         }
-        if(methodParameter.getMethod().isAnnotationPresent(AESEncryptBody.class)){
+        if (methodParameter.getMethod().isAnnotationPresent(AESEncryptBody.class)) {
             return EncryptAnnotationInfoBean.builder()
                     .encryptBodyMethod(EncryptBodyMethod.AES)
                     .key(methodParameter.getMethodAnnotation(AESEncryptBody.class).otherKey())
@@ -135,44 +152,44 @@ public class EncryptResponseBodyAdvice implements ResponseBodyAdvice {
 
     /**
      * 获取类控制器上的加密注解信息
+     *
      * @param clazz 控制器类
      * @return 加密注解信息
      */
-    private EncryptAnnotationInfoBean getClassAnnotation(Class clazz){
-        Annotation[] annotations = clazz.getDeclaredAnnotations();
-        if(annotations!=null && annotations.length>0){
-            for (Annotation annotation : annotations) {
-                if(annotation instanceof EncryptBody){
-                    EncryptBody encryptBody = (EncryptBody) annotation;
-                    return EncryptAnnotationInfoBean.builder()
-                            .encryptBodyMethod(encryptBody.value())
-                            .key(encryptBody.otherKey())
-                            .shaEncryptType(encryptBody.shaType())
-                            .build();
-                }
-                if(annotation instanceof MD5EncryptBody){
-                    return EncryptAnnotationInfoBean.builder()
-                            .encryptBodyMethod(EncryptBodyMethod.MD5)
-                            .build();
-                }
-                if(annotation instanceof SHAEncryptBody){
-                    return EncryptAnnotationInfoBean.builder()
-                            .encryptBodyMethod(EncryptBodyMethod.SHA)
-                            .shaEncryptType(((SHAEncryptBody) annotation).value())
-                            .build();
-                }
-                if(annotation instanceof DESEncryptBody){
-                    return EncryptAnnotationInfoBean.builder()
-                            .encryptBodyMethod(EncryptBodyMethod.DES)
-                            .key(((DESEncryptBody) annotation).otherKey())
-                            .build();
-                }
-                if(annotation instanceof AESEncryptBody){
-                    return EncryptAnnotationInfoBean.builder()
-                            .encryptBodyMethod(EncryptBodyMethod.AES)
-                            .key(((AESEncryptBody) annotation).otherKey())
-                            .build();
-                }
+    private EncryptAnnotationInfoBean getClassAnnotation(Class clazz) {
+        List<Annotation> annotations = new ArrayList<>(Arrays.asList(clazz.getSuperclass().getDeclaredAnnotations()));
+        annotations.addAll(Arrays.asList(clazz.getDeclaredAnnotations()));
+        for (Annotation annotation : annotations) {
+            if (annotation instanceof EncryptBody) {
+                EncryptBody encryptBody = (EncryptBody) annotation;
+                return EncryptAnnotationInfoBean.builder()
+                        .encryptBodyMethod(encryptBody.value())
+                        .key(encryptBody.otherKey())
+                        .shaEncryptType(encryptBody.shaType())
+                        .build();
+            }
+            if (annotation instanceof MD5EncryptBody) {
+                return EncryptAnnotationInfoBean.builder()
+                        .encryptBodyMethod(EncryptBodyMethod.MD5)
+                        .build();
+            }
+            if (annotation instanceof SHAEncryptBody) {
+                return EncryptAnnotationInfoBean.builder()
+                        .encryptBodyMethod(EncryptBodyMethod.SHA)
+                        .shaEncryptType(((SHAEncryptBody) annotation).value())
+                        .build();
+            }
+            if (annotation instanceof DESEncryptBody) {
+                return EncryptAnnotationInfoBean.builder()
+                        .encryptBodyMethod(EncryptBodyMethod.DES)
+                        .key(((DESEncryptBody) annotation).otherKey())
+                        .build();
+            }
+            if (annotation instanceof AESEncryptBody) {
+                return EncryptAnnotationInfoBean.builder()
+                        .encryptBodyMethod(EncryptBodyMethod.AES)
+                        .key(((AESEncryptBody) annotation).otherKey())
+                        .build();
             }
         }
         return null;
@@ -181,31 +198,32 @@ public class EncryptResponseBodyAdvice implements ResponseBodyAdvice {
 
     /**
      * 选择加密方式并进行加密
+     *
      * @param formatStringBody 目标加密字符串
-     * @param infoBean 加密信息
+     * @param infoBean         加密信息
      * @return 加密结果
      */
-    private String switchEncrypt(String formatStringBody,EncryptAnnotationInfoBean infoBean){
+    private String switchEncrypt(String formatStringBody, EncryptAnnotationInfoBean infoBean) {
         EncryptBodyMethod method = infoBean.getEncryptBodyMethod();
-        if(method==null){
+        if (method == null) {
             throw new EncryptMethodNotFoundException();
         }
-        if(method == EncryptBodyMethod.MD5){
+        if (method == EncryptBodyMethod.MD5) {
             return MD5EncryptUtil.encrypt(formatStringBody);
         }
-        if(method == EncryptBodyMethod.SHA){
+        if (method == EncryptBodyMethod.SHA) {
             SHAEncryptType shaEncryptType = infoBean.getShaEncryptType();
-            if(shaEncryptType==null) shaEncryptType = SHAEncryptType.SHA256;
-            return SHAEncryptUtil.encrypt(formatStringBody,shaEncryptType);
+            if (shaEncryptType == null) shaEncryptType = SHAEncryptType.SHA256;
+            return SHAEncryptUtil.encrypt(formatStringBody, shaEncryptType);
         }
         String key = infoBean.getKey();
-        if(method == EncryptBodyMethod.DES){
-            key = CheckUtils.checkAndGetKey(config.getDesKey(),key,"DES-KEY");
-            return DESEncryptUtil.encrypt(formatStringBody,key);
+        if (method == EncryptBodyMethod.DES) {
+            key = CheckUtils.checkAndGetKey(config.getDesKey(), key, "DES-KEY");
+            return DESEncryptUtil.encrypt(formatStringBody, key);
         }
-        if(method == EncryptBodyMethod.AES){
-            key = CheckUtils.checkAndGetKey(config.getAesKey(),key,"AES-KEY");
-            return AESEncryptUtil.encrypt(formatStringBody,key);
+        if (method == EncryptBodyMethod.AES) {
+            key = CheckUtils.checkAndGetKey(config.getAesKey(), key, "AES-KEY");
+            return AESEncryptUtil.encrypt(formatStringBody, key);
         }
         throw new EncryptBodyFailException();
     }

+ 2 - 0
src/main/java/com/izouma/jmrh/Application.java

@@ -1,6 +1,7 @@
 package com.izouma.jmrh;
 
 
+import cn.licoy.encryptbody.annotation.EnableEncryptBody;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@@ -10,6 +11,7 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
 @SpringBootApplication
 @EnableJpaAuditing
 @EnableSwagger2
+@EnableEncryptBody
 public class Application {
 
     public static void main(String[] args) {

+ 1 - 1
src/main/java/com/izouma/jmrh/config/WebMvcConfig.java

@@ -69,7 +69,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
                 .allowedHeaders("*")
                 .allowCredentials(true)
                 .allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH")
-                .exposedHeaders("Content-Disposition");
+                .exposedHeaders("Content-Disposition,Content-Encrypted");
     }
 
 }

+ 3 - 0
src/main/java/com/izouma/jmrh/web/AuthenticationController.java

@@ -1,5 +1,7 @@
 package com.izouma.jmrh.web;
 
+import cn.licoy.encryptbody.annotation.encrypt.EncryptBody;
+import cn.licoy.encryptbody.enums.EncryptBodyMethod;
 import com.izouma.jmrh.annotations.OperLog;
 import com.izouma.jmrh.domain.LoginLog;
 import com.izouma.jmrh.domain.MailCode;
@@ -39,6 +41,7 @@ import java.util.Objects;
 @AllArgsConstructor
 @RestController
 @RequestMapping("/auth")
+@EncryptBody(EncryptBodyMethod.AES)
 public class AuthenticationController {
     private AuthenticationManager authenticationManager;
     private JwtTokenUtil          jwtTokenUtil;

+ 3 - 0
src/main/java/com/izouma/jmrh/web/BaseController.java

@@ -1,5 +1,7 @@
 package com.izouma.jmrh.web;
 
+import cn.licoy.encryptbody.annotation.encrypt.EncryptBody;
+import cn.licoy.encryptbody.enums.EncryptBodyMethod;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.izouma.jmrh.annotations.Searchable;
 import com.izouma.jmrh.domain.*;
@@ -24,6 +26,7 @@ import java.util.regex.Pattern;
 
 @SuppressWarnings("ALL")
 @Slf4j
+@EncryptBody(EncryptBodyMethod.AES)
 public class BaseController {
     public static PageRequest toPageRequest(PageQuery pageQuery) {
         PageRequest pageRequest;

+ 2 - 0
src/main/java/com/izouma/jmrh/web/ResourceSupplyAndDemandController.java

@@ -1,5 +1,7 @@
 package com.izouma.jmrh.web;
 
+import cn.licoy.encryptbody.annotation.encrypt.EncryptBody;
+import cn.licoy.encryptbody.enums.EncryptBodyMethod;
 import com.izouma.jmrh.SndExcelDTO;
 import com.izouma.jmrh.annotations.OperLog;
 import com.izouma.jmrh.domain.BaseEntity;

+ 1 - 0
src/main/jmrh/package.json

@@ -12,6 +12,7 @@
     "axios": "^0.21.1",
     "babel-polyfill": "^6.26.0",
     "core-js": "^3.6.5",
+    "crypto-js": "^4.1.1",
     "date-fns": "^2.14.0",
     "element-ui": "^2.13.2",
     "normalize.css": "^8.0.1",

+ 25 - 0
src/main/jmrh/src/plugins/http.js

@@ -1,12 +1,29 @@
 import axios from 'axios';
 import router from '../router';
 import qs from 'qs';
+import CryptoJS from 'crypto-js';
 /* eslint-disable */
 let baseUrl = process.env.VUE_APP_BASE_URL;
 const axiosInstance = axios.create({
     baseURL: baseUrl
 });
 
+function decrypt(content) {
+    const key = CryptoJS.enc.Hex.parse('2181E9E80460B852859EE455AC5203D9');
+    const cipherText = CryptoJS.enc.Hex.parse(content);
+    var decrypted = CryptoJS.AES.decrypt(
+        {
+            ciphertext: cipherText
+        },
+        key,
+        {
+            mode: CryptoJS.mode.ECB,
+            padding: CryptoJS.pad.Pkcs7
+        }
+    );
+    return decrypted.toString(CryptoJS.enc.Utf8);
+}
+
 axiosInstance.interceptors.request.use(
     function(config) {
         config.headers = config.headers || {};
@@ -23,6 +40,14 @@ axiosInstance.interceptors.request.use(
 
 axiosInstance.interceptors.response.use(
     function(response) {
+        if (response.headers['content-encrypted'] === 'true') {
+            let decrypted = decrypt(response.data);
+            if (response.headers['content-type'].includes('application/json')) {
+                response.data = JSON.parse(decrypted);
+            } else {
+                response.data = decrypted;
+            }
+        }
         return response;
     },
     function(error) {

+ 1 - 0
src/main/jmrh/vue.config.js

@@ -1,6 +1,7 @@
 const path = require('path');
 module.exports = {
     publicPath: process.env.NODE_ENV === 'production' ? '/pc/' : '/',
+    productionSourceMap: false,
     devServer: {
         port: 8081
     },

+ 5 - 0
src/main/jmrh/yarn.lock

@@ -2836,6 +2836,11 @@ crypto-browserify@^3.11.0:
     randombytes "^2.0.0"
     randomfill "^1.0.3"
 
+crypto-js@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf"
+  integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==
+
 css-color-names@0.0.4, css-color-names@^0.0.4:
   version "0.0.4"
   resolved "https://registry.npm.taobao.org/css-color-names/download/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"

+ 2 - 6
src/main/resources/application.yaml

@@ -84,12 +84,8 @@ aliyun:
 general:
   host: http://art.izouma.com
 encrypt:
-  type: aes #Support base64 encoding, aes symmetric encryption, rsa asymmetric encryption, custom custom encryption method
-  privateKey: #rsaPrivate key
-  publicKey: #rsa
-  debug: false #debug mode is true without encryption
-  order: 1 #Encryption filter sequence number, if not filled, it is 0
-  secret: #aes encryption key
+  body:
+    aes-key: gmMRQzojuaSVGg3weNe6J5BCjDyH5BML
 ---
 
 spring:

+ 1 - 0
src/main/vue/package.json

@@ -17,6 +17,7 @@
     "chart.js": "^2.9.4",
     "clipboard": "^2.0.6",
     "core-js": "^3.6.5",
+    "crypto-js": "^4.1.1",
     "date-fns": "^2.14.0",
     "element-ui": "^2.13.2",
     "normalize.css": "^8.0.1",

+ 26 - 0
src/main/vue/src/plugins/http.js

@@ -1,11 +1,29 @@
 import axios from 'axios';
 import router from '../router';
 import qs from 'qs';
+import CryptoJS from 'crypto-js';
+
 let baseUrl = process.env.VUE_APP_BASE_URL; //如要修改本地开发地址,新建.env.development.local文件
 const axiosInstance = axios.create({
     baseURL: baseUrl
 });
 
+function decrypt(content) {
+    const key = CryptoJS.enc.Hex.parse('2181E9E80460B852859EE455AC5203D9');
+    const cipherText = CryptoJS.enc.Hex.parse(content);
+    var decrypted = CryptoJS.AES.decrypt(
+        {
+            ciphertext: cipherText
+        },
+        key,
+        {
+            mode: CryptoJS.mode.ECB,
+            padding: CryptoJS.pad.Pkcs7
+        }
+    );
+    return decrypted.toString(CryptoJS.enc.Utf8);
+}
+
 axiosInstance.interceptors.request.use(
     function(config) {
         config.headers = config.headers || {};
@@ -22,6 +40,14 @@ axiosInstance.interceptors.request.use(
 
 axiosInstance.interceptors.response.use(
     function(response) {
+        if (response.headers['content-encrypted'] === 'true') {
+            let decrypted = decrypt(response.data);
+            if (response.headers['content-type'].includes('application/json')) {
+                response.data = JSON.parse(decrypted);
+            } else {
+                response.data = decrypted;
+            }
+        }
         return response;
     },
     function(error) {

+ 1 - 0
src/main/vue/vue.config.js

@@ -1,6 +1,7 @@
 const path = require('path');
 module.exports = {
     publicPath: process.env.NODE_ENV === 'production' ? '/admin/' : '/',
+    productionSourceMap: false,
     devServer: {
         port: 8081
     },

+ 5 - 0
src/main/vue/yarn.lock

@@ -2730,6 +2730,11 @@ crypto-browserify@^3.11.0:
     randombytes "^2.0.0"
     randomfill "^1.0.3"
 
+crypto-js@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf"
+  integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==
+
 css-color-names@0.0.4, css-color-names@^0.0.4:
   version "0.0.4"
   resolved "https://registry.npm.taobao.org/css-color-names/download/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"

+ 26 - 0
src/test/java/com/izouma/jmrh/CommonTest.java

@@ -1,5 +1,6 @@
 package com.izouma.jmrh;
 
+import cn.licoy.encryptbody.util.Hex2Util;
 import com.izouma.jmrh.domain.BaseEntity;
 import com.izouma.jmrh.domain.User;
 import com.izouma.jmrh.web.BaseController;
@@ -14,6 +15,9 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.crypto.*;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
 import java.awt.*;
 import java.awt.font.FontRenderContext;
 import java.awt.geom.AffineTransform;
@@ -22,6 +26,10 @@ import java.lang.reflect.Method;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
 import java.time.YearMonth;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
@@ -144,4 +152,22 @@ public class CommonTest {
     public void testEnum() {
         System.out.println(ChronoUnit.MONTHS.between(YearMonth.of(2021, 1), YearMonth.of(2021, 10)));
     }
+
+    @Test
+    public void testSecureRandom() throws Exception {
+        String password = "gmMRQzojuaSVGg3weNe6J5BCjDyH5BML";
+
+        KeyGenerator generator = KeyGenerator.getInstance("AES");
+        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
+        random.setSeed(password.getBytes());
+        generator.init(128, random);
+        SecretKey secretKey = generator.generateKey();
+        byte[] enCodeFormat = secretKey.getEncoded();
+        SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
+        System.out.println("key:" + Hex2Util.parseByte2HexStr(key.getEncoded()));
+        Cipher cipher = Cipher.getInstance("AES");
+        cipher.init(Cipher.ENCRYPT_MODE, key);
+        byte[] byteContent = "hello".getBytes(StandardCharsets.UTF_8);
+        System.out.println("encrypted: " + Hex2Util.parseByte2HexStr(cipher.doFinal(byteContent)));
+    }
 }