From 108f24d7d1e53e18c2c35cf4a388d0298aaea555 Mon Sep 17 00:00:00 2001 From: q18idc Date: Tue, 23 Mar 2021 12:58:43 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=93=8D=E5=BA=94=E5=8A=A0=E5=AF=86=E8=A7=A3=E5=AF=86=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scanner/api/annotation/PostResource.java | 5 + kernel-d-security/pom.xml | 1 + .../pom.xml | 49 +++++ .../advice/EncryptionRequestBodyAdvice.java | 184 ++++++++++++++++++ .../advice/EncryptionResponseBodyAdvice.java | 96 +++++++++ .../constants/EncryptionConstants.java | 32 +++ .../exception/EncryptionException.java | 24 +++ .../enums/EncryptionExceptionEnum.java | 48 +++++ .../encrypt/holder/EncryptionHolder.java | 43 ++++ 9 files changed, 482 insertions(+) create mode 100644 kernel-d-security/security-sdk-request-encrypt-and-decode/pom.xml create mode 100644 kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionRequestBodyAdvice.java create mode 100644 kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionResponseBodyAdvice.java create mode 100644 kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/constants/EncryptionConstants.java create mode 100644 kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/exception/EncryptionException.java create mode 100644 kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/exception/enums/EncryptionExceptionEnum.java create mode 100644 kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/holder/EncryptionHolder.java diff --git a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/annotation/PostResource.java b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/annotation/PostResource.java index 3a87f2e49..704eae66c 100644 --- a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/annotation/PostResource.java +++ b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/annotation/PostResource.java @@ -77,6 +77,11 @@ public @interface PostResource { */ boolean requiredPermission() default true; + /** + * 是否需要请求解密,响应加密 (true-需要,false-不需要) + */ + boolean requiredEncryption() default false; + /** * 是否是视图类型:true-是,false-否 * 如果是视图类型,url需要以 '/view' 开头, diff --git a/kernel-d-security/pom.xml b/kernel-d-security/pom.xml index ed5e65408..41dab0480 100644 --- a/kernel-d-security/pom.xml +++ b/kernel-d-security/pom.xml @@ -21,6 +21,7 @@ security-sdk-captcha security-sdk-count security-sdk-xss + security-sdk-request-encrypt-and-decode security-spring-boot-starter diff --git a/kernel-d-security/security-sdk-request-encrypt-and-decode/pom.xml b/kernel-d-security/security-sdk-request-encrypt-and-decode/pom.xml new file mode 100644 index 000000000..99e67610e --- /dev/null +++ b/kernel-d-security/security-sdk-request-encrypt-and-decode/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + + cn.stylefeng.roses + kernel-d-security + 7.0.2 + ../pom.xml + + + security-sdk-request-encrypt-and-decode + + jar + + + + + + org.bouncycastle + bcprov-jdk15to18 + 1.68 + + + + + cn.stylefeng.roses + security-api + 7.0.2 + + + + + cn.stylefeng.roses + scanner-api + 7.0.2 + + + + + org.springframework.boot + spring-boot-starter-web + + + + + diff --git a/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionRequestBodyAdvice.java b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionRequestBodyAdvice.java new file mode 100644 index 000000000..ea1e7549c --- /dev/null +++ b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionRequestBodyAdvice.java @@ -0,0 +1,184 @@ +package cn.stylefeng.roses.kernel.security.request.encrypt.advice; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.HexUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.crypto.symmetric.AES; +import cn.stylefeng.roses.kernel.scanner.api.annotation.PostResource; +import cn.stylefeng.roses.kernel.security.request.encrypt.constants.EncryptionConstants; +import cn.stylefeng.roses.kernel.security.request.encrypt.exception.EncryptionException; +import cn.stylefeng.roses.kernel.security.request.encrypt.exception.enums.EncryptionExceptionEnum; +import cn.stylefeng.roses.kernel.security.request.encrypt.holder.EncryptionHolder; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.springframework.core.MethodParameter; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.security.Security; +import java.util.Date; + +/** + * 请求参数解密 + * + * @author luojie + * @date 2021/3/23 12:53 + */ +@Slf4j +@ControllerAdvice +public class EncryptionRequestBodyAdvice implements RequestBodyAdvice { + + static { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + // 添加PKCS7Padding支持 + Security.addProvider(new com.sun.crypto.provider.SunJCE()); + Security.addProvider(new BouncyCastleProvider()); + } + } + + /** + * 设置条件,这个条件为true才会执行下面的beforeBodyRead方法 + * + * @param methodParameter + * @param targetType + * @param converterType + * @return + */ + @Override + public boolean supports(MethodParameter methodParameter, Type targetType, Class> converterType) { + // 判断当前请求的接口中是否有 PostResource 注解 + Annotation[] annotations = methodParameter.getAnnotatedElement().getAnnotations(); + for (Annotation annotation : annotations) { + Class annotationType = annotation.annotationType(); + return PostResource.class.equals(annotationType); + } + return false; + } + + @Override + public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> converterType) throws IOException { + Method method = parameter.getMethod(); + if (method != null) { + PostResource annotation = method.getAnnotation(PostResource.class); + + if (annotation != null) { + // 是否需要加密 + if (annotation.requiredEncryption()) { + return new HttpInputMessage() { + @Override + public HttpHeaders getHeaders() { + return inputMessage.getHeaders(); + } + + @Override + public InputStream getBody() throws IOException { + + // 获取请求的内容 + InputStream body = inputMessage.getBody(); + String bodyStr = IoUtil.readUtf8(body); + + //JSON 解析请求中的内容 + JSONObject jsonObject = null; + try { + jsonObject = JSON.parseObject(bodyStr); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + log.error(StrUtil.format("请求的内容:{}", bodyStr)); + // 请求json解析异常 + throw new EncryptionException(EncryptionExceptionEnum.REQUEST_JSON_PARSE_ERROR); + } + + // 使用私钥解密出返回加密数据的key和请求的内容 + RSA rsa = new RSA(EncryptionConstants.PRIVATE_KEY, EncryptionConstants.PUBLIC_KEY); + + // 请求中RSA公钥加密后的key 用于将返回的内容AES加密 + String key = jsonObject.getString("key"); + + // 获取请求中 AES 加密后的数据 + String data = jsonObject.getString("data"); + + if (StrUtil.isBlank(key) || StrUtil.isBlank(data)) { + // 请求的json格式错误,未包含加密的data字段数据以及加密的key字段 + throw new EncryptionException(EncryptionExceptionEnum.REQUEST_JSON_ERROR); + } + + String aesKey = null; + try { + // 使用 RSA 私钥解密请求中公钥加密后的key + aesKey = rsa.decryptStr(key, KeyType.PrivateKey, CharsetUtil.CHARSET_UTF_8); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + // 解密失败 + throw new EncryptionException(EncryptionExceptionEnum.RSA_DECRYPT_ERROR); + } + + byte[] iv = HexUtil.decodeHex(SecureUtil.md5(StrUtil.format("{}{}", aesKey, DateUtil.format(new Date(), "yyyyMMdd")))); + byte[] aesKeyByte = Base64.decode(aesKey); + + AES aes = new AES("CFB", "PKCS7Padding", aesKeyByte, iv); + String reqData = null; + try { + // 使用aes解密请求的内容 + reqData = aes.decryptStr(data); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + // 解密失败 + throw new EncryptionException(EncryptionExceptionEnum.RSA_DECRYPT_ERROR); + } + + log.info(StrUtil.format("返回数据加密的key:{}", aesKey)); + + // 将 AES KEY 放到 ThreadLocal 中 + EncryptionHolder.setAesKey(aesKey); + + return new ByteArrayInputStream(reqData.getBytes(CharsetUtil.CHARSET_UTF_8)); + } + }; + } + } + } + + return inputMessage; + + } + + @Override + public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> converterType) { + return body; + } + + /** + * 传入的json是空值的时候,进入这个方法 + * + * @param body + * @param inputMessage + * @param parameter + * @param targetType + * @param converterType + * @return + */ + @Override + public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> converterType) { + return body; + } +} diff --git a/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionResponseBodyAdvice.java b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionResponseBodyAdvice.java new file mode 100644 index 000000000..22e819e02 --- /dev/null +++ b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionResponseBodyAdvice.java @@ -0,0 +1,96 @@ +package cn.stylefeng.roses.kernel.security.request.encrypt.advice; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.HexUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.symmetric.AES; +import cn.stylefeng.roses.kernel.rule.pojo.response.ResponseData; +import cn.stylefeng.roses.kernel.scanner.api.annotation.PostResource; +import cn.stylefeng.roses.kernel.security.request.encrypt.holder.EncryptionHolder; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +import java.security.Security; +import java.util.Date; + +/** + * 响应结果加密 + * + * @author luojie + * @date 2021/3/23 12:54 + */ +@Slf4j +@ControllerAdvice +public class EncryptionResponseBodyAdvice implements ResponseBodyAdvice { + + static { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + // 添加PKCS7Padding支持 + Security.addProvider(new com.sun.crypto.provider.SunJCE()); + Security.addProvider(new BouncyCastleProvider()); + } + } + + @Override + public boolean supports(MethodParameter returnType, Class converterType) { + return true; + } + + @Override + public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { + if (null != body) { + + PostResource annotation = returnType.getMethod().getAnnotation(PostResource.class); + + if (annotation != null) { + if (annotation.requiredEncryption()) { + // 判断响应实体是否是 ResponseData + if (body instanceof ResponseData) { + // 转换类型 + ResponseData responseData = (ResponseData)body; + + Object data = responseData.getData(); + + // 将返回的数据格式化成json字符串 + String respJsonStr = JSON.toJSONString(data); + + // 从 ThreadLocal 中获取 aes key + String aesKey = EncryptionHolder.getAesKey(); + // 偏移 + byte[] iv = HexUtil.decodeHex(SecureUtil.md5(StrUtil.format("{}{}", aesKey, DateUtil.format(new Date(), "yyyyMMdd")))); + + if (StrUtil.isNotBlank(aesKey)) { + + byte[] keyByte = Base64.decode(aesKey.getBytes(CharsetUtil.CHARSET_UTF_8)); + + // AES + AES aes = new AES("CFB", "PKCS7Padding", keyByte, iv); + String encryptBase64 = aes.encryptBase64(respJsonStr); + + responseData.setData(encryptBase64); + + } + + // 清除当前线程中的aes key + EncryptionHolder.clearAesKey(); + + return responseData; + + } + } + } + + } + return body; + } +} diff --git a/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/constants/EncryptionConstants.java b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/constants/EncryptionConstants.java new file mode 100644 index 000000000..b6cb218a5 --- /dev/null +++ b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/constants/EncryptionConstants.java @@ -0,0 +1,32 @@ +package cn.stylefeng.roses.kernel.security.request.encrypt.constants; + +/** + * 请求解密,响应加密 常量 + * + * @author luojie + * @date 2021/3/23 12:54 + */ +public interface EncryptionConstants { + + /** + * 公钥 + */ + String PUBLIC_KEY = "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwiRWfPs7Qds9K+gbabarxKOn71vZY9S7C5cmiTTmDoxvL8gixtlldbqaBCG0fhP48qxImw4jmAnXbSb51hDohTRXmGIuBEQuhIWwh28rorSiGuOye6PTbYNuup5CWwxMkD/ARHrs5Cvg9+vJTHXdg3TrRbwiW6GniDvVGPcl0d9TshX5Dgo6m9VZkLJfHJkVKmjAOOvede8uPgaM1ymt6JexjTcn6uiIrWlDkKTzvAq+Hb9cj9tz/Q5FKo17TF7oa4XC8lfximCzAvMwsl/kmqfh0cSNqeoW9s8LcjY0o7YexYJB3+jjp88QbzqXnUNYMVGz0M2cYLmAaM2LVwWvVrfs1HCB1o+WqGqjaBql/4apVyhqf77Py6M+2WUr1yKgDfRXjZ1h9w9e3jh3oQdjSk36fboJmHXBnKwwoecxW5csVJmj/M7CPP7Xw8BPGV4/rMmRKjBRTv5XdcRnnMm8nd8EdK/2AaXZqu0O2iRjQlFFgLNUzP+eudvLCA0+Dxczkpgvcr7S4oQtD+TCFm1gWaC+Kho5liMQ7OV0L7tL8O+tzzbKmoVj/fg8uIG5ljqsU6rE5WFUCl1w0v8Gzx1Yz3IXaRKmXTgmxZPdaN3nVA+YBT+N3EKQETVlN6w+65/HK/8ZsB36exrlkkxUo+y0umk0DzFFY/9i1x2kU7wXPzECAwEAAQ=="; + + /** + * 私钥 + */ + String PRIVATE_KEY = + "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"; + + /** + * 模块名 + */ + String ENCRYPTION_MODULE_NAME = "kernel-d-encryption"; + + /** + * 异常枚举的步进值 + */ + String ENCRYPTION_EXCEPTION_STEP_CODE = "29"; + +} diff --git a/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/exception/EncryptionException.java b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/exception/EncryptionException.java new file mode 100644 index 000000000..984b8c14d --- /dev/null +++ b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/exception/EncryptionException.java @@ -0,0 +1,24 @@ +package cn.stylefeng.roses.kernel.security.request.encrypt.exception; + +import cn.hutool.core.util.StrUtil; +import cn.stylefeng.roses.kernel.rule.exception.AbstractExceptionEnum; +import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException; +import cn.stylefeng.roses.kernel.security.request.encrypt.constants.EncryptionConstants; + +/** + * 请求解密,响应加密 异常 + * + * @author luojie + * @date 2021/3/23 12:54 + */ +public class EncryptionException extends ServiceException { + + public EncryptionException(AbstractExceptionEnum exception, Object... params) { + super(EncryptionConstants.ENCRYPTION_MODULE_NAME, exception.getErrorCode(), StrUtil.format(exception.getUserTip(), params)); + } + + public EncryptionException(AbstractExceptionEnum exception) { + super(EncryptionConstants.ENCRYPTION_MODULE_NAME, exception); + } + +} diff --git a/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/exception/enums/EncryptionExceptionEnum.java b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/exception/enums/EncryptionExceptionEnum.java new file mode 100644 index 000000000..6ef3a104d --- /dev/null +++ b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/exception/enums/EncryptionExceptionEnum.java @@ -0,0 +1,48 @@ +package cn.stylefeng.roses.kernel.security.request.encrypt.exception.enums; + +import cn.stylefeng.roses.kernel.rule.constants.RuleConstants; +import cn.stylefeng.roses.kernel.rule.exception.AbstractExceptionEnum; +import cn.stylefeng.roses.kernel.security.request.encrypt.constants.EncryptionConstants; +import lombok.Getter; + +/** + * 请求解密,响应加密 异常枚举 + * + * @author luojie + * @date 2021/3/23 12:54 + */ +@Getter +public enum EncryptionExceptionEnum implements AbstractExceptionEnum { + + /** + * 请求的json解析异常 + */ + REQUEST_JSON_PARSE_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + EncryptionConstants.ENCRYPTION_EXCEPTION_STEP_CODE + "01", "请求的json解析异常"), + + /** + * 请求的json格式错误,未包含加密的data字段数据以及加密的key字段 + */ + REQUEST_JSON_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + EncryptionConstants.ENCRYPTION_EXCEPTION_STEP_CODE + "02", "请求的json格式错误,未包含加密的data字段数据以及加密的key字段"), + + /** + * 解密失败 + */ + RSA_DECRYPT_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + EncryptionConstants.ENCRYPTION_EXCEPTION_STEP_CODE + "03", "解密失败"), + + ; + + /** + * 错误编码 + */ + private final String errorCode; + + /** + * 提示用户信息 + */ + private final String userTip; + + EncryptionExceptionEnum(String errorCode, String userTip) { + this.errorCode = errorCode; + this.userTip = userTip; + } +} diff --git a/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/holder/EncryptionHolder.java b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/holder/EncryptionHolder.java new file mode 100644 index 000000000..c10b73365 --- /dev/null +++ b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/holder/EncryptionHolder.java @@ -0,0 +1,43 @@ +package cn.stylefeng.roses.kernel.security.request.encrypt.holder; + +/** + * 用于存储响应加密秘钥 + * + * @author luojie + * @date 2021/3/23 12:54 + */ +public class EncryptionHolder { + + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置 + * + * @param aesKey aesKey + * @date 2020/8/24 + */ + public static void setAesKey(String aesKey) { + CONTEXT_HOLDER.set(aesKey); + } + + /** + * 获取 + * + * @author fengshuonan + * @date 2020/8/24 + */ + public static String getAesKey() { + return CONTEXT_HOLDER.get(); + } + + /** + * 清除 + * + * @author fengshuonan + * @date 2020/8/24 + */ + public static void clearAesKey() { + CONTEXT_HOLDER.remove(); + } + +} From 62423cf4b432f7bcd2c8fe75b20428d8296cac19 Mon Sep 17 00:00:00 2001 From: q18idc Date: Tue, 23 Mar 2021 13:57:47 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E3=80=907.0.2=E3=80=91=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=93=8D=E5=BA=94=E5=8A=A0=E5=AF=86=E8=A7=A3?= =?UTF-8?q?=E5=AF=86=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel-d-security/security-spring-boot-starter/pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel-d-security/security-spring-boot-starter/pom.xml b/kernel-d-security/security-spring-boot-starter/pom.xml index 35fcd9f7b..7838ee498 100644 --- a/kernel-d-security/security-spring-boot-starter/pom.xml +++ b/kernel-d-security/security-spring-boot-starter/pom.xml @@ -45,6 +45,13 @@ 7.0.2 + + + cn.stylefeng.roses + security-sdk-request-encrypt-and-decode + 7.0.2 + + From 63256ba0fefa43bb11b2287a11411059329c5b36 Mon Sep 17 00:00:00 2001 From: q18idc Date: Wed, 24 Mar 2021 23:12:59 +0800 Subject: [PATCH 3/4] update --- .../advice/EncryptionRequestBodyAdvice.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionRequestBodyAdvice.java b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionRequestBodyAdvice.java index ea1e7549c..8dfef772c 100644 --- a/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionRequestBodyAdvice.java +++ b/kernel-d-security/security-sdk-request-encrypt-and-decode/src/main/java/cn/stylefeng/roses/kernel/security/request/encrypt/advice/EncryptionRequestBodyAdvice.java @@ -6,10 +6,13 @@ import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.HexUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.Mode; +import cn.hutool.crypto.Padding; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.RSA; import cn.hutool.crypto.symmetric.AES; +import cn.hutool.crypto.symmetric.SM4; import cn.stylefeng.roses.kernel.scanner.api.annotation.PostResource; import cn.stylefeng.roses.kernel.security.request.encrypt.constants.EncryptionConstants; import cn.stylefeng.roses.kernel.security.request.encrypt.exception.EncryptionException; @@ -109,6 +112,26 @@ public class EncryptionRequestBodyAdvice implements RequestBodyAdvice { // 使用私钥解密出返回加密数据的key和请求的内容 RSA rsa = new RSA(EncryptionConstants.PRIVATE_KEY, EncryptionConstants.PUBLIC_KEY); + // 先使用SM4解密出请求的json + String objectString = jsonObject.getString("data"); + if (StrUtil.isBlank(objectString)) { + // 请求json解析异常 + throw new EncryptionException(EncryptionExceptionEnum.REQUEST_JSON_PARSE_ERROR); + } + + String sm4Key = SecureUtil.md5(DateUtil.format(new Date(), "yyyyMMdd")); + SM4 sm4 = new SM4(Mode.ECB, Padding.PKCS5Padding, HexUtil.decodeHex(sm4Key)); + try { + String decryptStr = sm4.decryptStr(objectString); + jsonObject = JSON.parseObject(decryptStr); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + // 解密失败 + throw new EncryptionException(EncryptionExceptionEnum.RSA_DECRYPT_ERROR); + + } + // 请求中RSA公钥加密后的key 用于将返回的内容AES加密 String key = jsonObject.getString("key"); @@ -124,6 +147,7 @@ public class EncryptionRequestBodyAdvice implements RequestBodyAdvice { try { // 使用 RSA 私钥解密请求中公钥加密后的key aesKey = rsa.decryptStr(key, KeyType.PrivateKey, CharsetUtil.CHARSET_UTF_8); + log.info("本次请求数据AES加密的KEY为:" + aesKey); } catch (Exception e) { e.printStackTrace(); log.error(e.getMessage()); @@ -139,6 +163,7 @@ public class EncryptionRequestBodyAdvice implements RequestBodyAdvice { try { // 使用aes解密请求的内容 reqData = aes.decryptStr(data); + log.info(StrUtil.format("本次请求的内容:{}", reqData)); } catch (Exception e) { e.printStackTrace(); log.error(e.getMessage()); From 3f7d2b832e1932e83148758cb2b69aeeecbf5753 Mon Sep 17 00:00:00 2001 From: 18idc <993143799@qq.com> Date: Sat, 27 Mar 2021 23:24:29 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../README.md | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 kernel-d-security/security-sdk-request-encrypt-and-decode/README.md diff --git a/kernel-d-security/security-sdk-request-encrypt-and-decode/README.md b/kernel-d-security/security-sdk-request-encrypt-and-decode/README.md new file mode 100644 index 000000000..9c00f8fab --- /dev/null +++ b/kernel-d-security/security-sdk-request-encrypt-and-decode/README.md @@ -0,0 +1,35 @@ +请求解密,响应加密模块 + +大致流程 生成随机key并使用AES加密 模式为CFB 填充为Pkcs7 加密请求的内容,并使用RSA公钥加密生成的key,用于后端进行解密,后端再根据这个key加密响应结果 + +前端实例代码 + +```javascript + layui.use(['HttpEncryptionRequest'], function () { + var result = new HttpEncryptionRequest(Feng.ctxPath + '/encode', function (res) { + console.log(res) + }) + result.set({name:'测试'}); + result.start(); + }) +``` + +后端示例代码 + +在 PostResource 注解中 requiredEncryption 参数设置为true 开启参数解密,响应加密 接收参数实体加上 @RequestBody 注解 + +```java +/** + * 示例加密方法 + *

+ * requiredEncryption = true + *

+ * + * @author luojie + * @date 2021/3/27 22:31 + */ + @PostResource(name = "示例加密方法", path = "/encode", requiredPermission = false, requiredLogin = false, requiredEncryption = true) + public ResponseData encode(@RequestBody Dict dict) { + return new SuccessResponseData(dict); + } +``` \ No newline at end of file