mirror of https://gitee.com/stylefeng/roses
【8.1.8】【security】更新requestBody解密过程
parent
1b3da64b1c
commit
d2c0b5b9b1
|
@ -1,198 +0,0 @@
|
|||
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.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.exception.EncryptionException;
|
||||
import cn.stylefeng.roses.kernel.security.request.encrypt.exception.enums.EncryptionExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.security.request.encrypt.holder.EncryptionHolder;
|
||||
import cn.stylefeng.roses.kernel.security.request.encrypt.holder.EncryptionRsaHolder;
|
||||
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
|
||||
* @since 2021/3/23 12:53
|
||||
*/
|
||||
@Slf4j
|
||||
@ControllerAdvice
|
||||
@SuppressWarnings("all")
|
||||
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方法
|
||||
*
|
||||
* @author luojie
|
||||
* @since 2021/10/29 9:32
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
// 判断当前请求的接口中是否有 PostResource 注解
|
||||
Annotation[] annotations = methodParameter.getAnnotatedElement().getAnnotations();
|
||||
for (Annotation annotation : annotations) {
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
return PostResource.class.equals(annotationType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> 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 = EncryptionRsaHolder.STATIC_RSA;
|
||||
|
||||
// 先使用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");
|
||||
|
||||
// 获取请求中 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);
|
||||
log.info("本次请求数据AES加密的KEY为:" + aesKey);
|
||||
} 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);
|
||||
log.info(StrUtil.format("本次请求的内容:{}", reqData));
|
||||
} 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<? extends HttpMessageConverter<?>> converterType) {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return body;
|
||||
}
|
||||
}
|
|
@ -8,17 +8,6 @@ package cn.stylefeng.roses.kernel.security.request.encrypt.constants;
|
|||
*/
|
||||
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 =
|
||||
"MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDCJFZ8+ztB2z0r6BtptqvEo6fvW9lj1LsLlyaJNOYOjG8vyCLG2WV1upoEIbR+E/jyrEibDiOYCddtJvnWEOiFNFeYYi4ERC6EhbCHbyuitKIa47J7o9Ntg266nkJbDEyQP8BEeuzkK+D368lMdd2DdOtFvCJboaeIO9UY9yXR31OyFfkOCjqb1VmQsl8cmRUqaMA469517y4+BozXKa3ol7GNNyfq6IitaUOQpPO8Cr4dv1yP23P9DkUqjXtMXuhrhcLyV/GKYLMC8zCyX+Sap+HRxI2p6hb2zwtyNjSjth7FgkHf6OOnzxBvOpedQ1gxUbPQzZxguYBozYtXBa9Wt+zUcIHWj5aoaqNoGqX/hqlXKGp/vs/Loz7ZZSvXIqAN9FeNnWH3D17eOHehB2NKTfp9ugmYdcGcrDCh5zFblyxUmaP8zsI8/tfDwE8ZXj+syZEqMFFO/ld1xGecybyd3wR0r/YBpdmq7Q7aJGNCUUWAs1TM/56528sIDT4PFzOSmC9yvtLihC0P5MIWbWBZoL4qGjmWIxDs5XQvu0vw763PNsqahWP9+Dy4gbmWOqxTqsTlYVQKXXDS/wbPHVjPchdpEqZdOCbFk91o3edUD5gFP43cQpARNWU3rD7rn8cr/xmwHfp7GuWSTFSj7LS6aTQPMUVj/2LXHaRTvBc/MQIDAQABAoICAEmcX5K562j4CMSqGCLIGW7Qoq82A0I/+b/WSs1BWm1vwAS8/Lqq2TZ/T0B7yyT2y7CvtEPeY46VRLJlUdthw9gl1YS4zTve4khrLFjdxQzHMqPBa/5HSrY+XHCz6vL8wdb75RnSBhoprP4zz7qHAjuCzGY/hEN8Erqr/QufQh7Gb8SZYjp5lGlbxr7/cuK0BHt1BlVO0aWjz8PC1goLCp6jTbX+kMsX6iLeQ7kHYkcNR7KdySneiqcLjxwoUvTNE5EZQaaDw9i0+4sFzJQFmfct6oiP3yrq0FQ6gtuGRAVC234F+pY8hn0tHM0fyTbevwTMNsLYAxAFpfOW49tWE3nD4WxH2x/Xm4FUunfdJkMwsANBEnm4mz98whHW9SoH+ujuSvAFsBBwrUJcjCwn0DS1n9fqLrGBEtyVyh0GlbdUdHGZDqtbPCf7BhJpVVMYCKUP2fKPQAD13r+5rmv2wJllCfziIlMy3C5rUXcShDmPWdaY3b05r+QNIaIUgkNoblCO1sLsMgvL1Sber/z6lgSq/jxud3ja+JgwrA9jQBZQwOvddRUpi0VCSIDwgtsVzJNHsCQRnsHKUs5THglZO4QxcQX2yHXqlnyBm3VyxVGq3b8mrUdsnzhW2YV2e+HbnVN+Nx1cbexdedj3bsOGMUTFxW/FH97mUGus/HShsuYBAoIBAQDjbjQ2LqAaPdUe49vzUPxFBb4/R0m5jf1daP6tpuTZ3HOtR97M6amCIdyvLaw1AqIQlQ0QocPuw9LeWmeL0xYPqbXfrKi2/VXfPCdEgFn6POiuC7vFjVDSVZQDdSG4CWH4hr/CGSrBTqzbmXETYfXhPhOcQpw66eGfflnQk9myz9UpLRQ+E+ERKLu6OumhgvPeMgn2D2eMupIUIYrSrWKImw0f9mqNoylybK2AAcrUqMEJdoM7dHWXBefPGhAJL4CQv+we8TK8v0gaNz2pvGoCUtdSc+QBTk9+IKXeYyJNISpyvfcNvXGK+PgMjof+wkWgMZip9fMbAGDUSTtDuNzhAoIBAQDah6DdGzkmaGHi92PF2eMnuhPOGNQY14brZY3Pe3VL0GtmfqUXx/K4O4rtwtCWyShW5hv8y7vtkR0XQOtx6YRxeeIZCuikQVyeVcH4IJWHbrt9XyTeD/Lxm2ZETI6HO4AAdYHJo0xOKE33lKSKlsCO8R9SWLqY86EOfSE6nHdcRjlmEu1iilH2YZEliw/2/W9PW/KuPHcRs6uv+NGGP7KlwodCcvLWlf7b8RR2yMQ2CjzMrzyzqijuOCSFFu3hBcLVDIkElA7EwIAZ7nV6/3HVUIhDFFK1n8kXcMiSGiHIbpGT/wxnH90StXM99AGGe+YG31EIcS421x/jTPcByNxRAoIBAQCI7734MbKsmjZMVx4ELur2FDMsnpvBYcEAEUvm+uooUxhDaVa5QqeRdxoNUA60DFXQbi5jqULz7Gx2/TADfKF35NNhTfB33alqtClgkXebuDjRMrdoh2H2gxiPzGL1EJEwttGW6NhZdCmYP5dZ+E23xUzBdUnkHxZ+lfE2KQ+XHpRWKpJZnlaRolkGFJq/aL21N6PPyA6tKVjzTg7sMwF1BwasDA60IV2/S7hbrriVutYgAH+buM9kk2WzyRmGrldW0Hg3WTsXcoTTZBd4r72UkJSdTLIoJyKt6rJ0aHQqxKFuXPr4BuzqpGWWCevQdOC/R52IGFK8G0oyB7XrXM+BAoIBAD2QXhpMVBJk78bASURw+NS1UGUMi5wgA+uHJadhMY9VPRyX6yzC8LdEVwRakOcZ7ppko1fZka0A58AoUuw5jE1nt/G0KAw2OcCFimq7y0RnRrywNDO3LIsya2Isay7f7VSzxgenUJToN+ba4mwEwmTCuz84rgDvCd2KFPVtJRdC1WLTTDspmqOdowV/otTDWztxPPInKKg9BM5De8ulYE/geLiYp58ajL0rsscwEk7jHXPQnnpDItrRyEASUJvHQrdAm81FZM+7J5ummUQ4eLpOwMSdEhwG0uEerfKzF/deZvbZsIXQ7TgbFEdM2a0odIpVGYAWWp2qh1pC0YeYLbECggEBAL091vjMKLk7SBlpGKmwHS5Eo+94Fc6g1yC2+XbOsedo88inoEBlr52Ld+pKgGPGseJvT1Zf9WtcgVMvCcJ3Czc/uvRKoc9iAaWx1fPdZeuiojPQQDmbLMTgx4s4gp8Lk8uTiVoZ3piUgfESDEw+ukcTSTmMccrlGk5QXAvmtmAJzXMjHltldvOqF51E2Gu2lAcfKYy8aOdL4buavNEfHwKt9zIwnoP5EuSdfNwUkjTNe3YKZqXN8uodc7SvYYzIYBG4FwJPCiF8D+WSpXNr3MB+ff24d4CwQSNt5MNjeCjDuTE0VWf/aTyDL/454STc4gzyO5I9Bjwrs44WrS5krIA=";
|
||||
|
||||
/**
|
||||
* 模块名
|
||||
*/
|
||||
|
|
|
@ -25,11 +25,9 @@ public enum EncryptionExceptionEnum implements AbstractExceptionEnum {
|
|||
REQUEST_JSON_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + EncryptionConstants.ENCRYPTION_EXCEPTION_STEP_CODE + "02", "请求的json格式错误,未包含加密的data字段数据以及加密的key字段"),
|
||||
|
||||
/**
|
||||
* 解密失败
|
||||
* 请求解密失败,原始数据格式错误,原始数据要符合EncryptionDTO类的规范
|
||||
*/
|
||||
RSA_DECRYPT_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + EncryptionConstants.ENCRYPTION_EXCEPTION_STEP_CODE + "03", "解密失败"),
|
||||
|
||||
;
|
||||
RSA_DECRYPT_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + EncryptionConstants.ENCRYPTION_EXCEPTION_STEP_CODE + "03", "请求解密失败,原始数据格式错误");
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
package cn.stylefeng.roses.kernel.security.request.encrypt.holder;
|
||||
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import cn.stylefeng.roses.kernel.security.request.encrypt.constants.EncryptionConstants;
|
||||
|
||||
/**
|
||||
* 用于存储RSA实例
|
||||
*
|
||||
* @author luojie
|
||||
* @since 2021/6/4 08:58
|
||||
*/
|
||||
public class EncryptionRsaHolder {
|
||||
|
||||
public static RSA STATIC_RSA = new RSA(EncryptionConstants.PRIVATE_KEY, EncryptionConstants.PUBLIC_KEY);
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package cn.stylefeng.roses.kernel.security.request.encrypt.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 加密数据包装的DTO
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @since 2024/6/28 14:36
|
||||
*/
|
||||
@Data
|
||||
public class EncryptionDTO {
|
||||
|
||||
/**
|
||||
* 数据传输的秘钥,此秘钥已经进行了SM2非对称加密,需要SM2解密才能看到真实的秘钥
|
||||
*/
|
||||
private String passedKey;
|
||||
|
||||
/**
|
||||
* 数据传输的真实数据,这个数据经过了SM4对称加密,秘钥是passedKey(SM2解密后)
|
||||
*/
|
||||
private String passedData;
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package cn.stylefeng.roses.kernel.security.request.encrypt.request;
|
||||
|
||||
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SmUtil;
|
||||
import cn.hutool.crypto.symmetric.SM4;
|
||||
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
|
||||
import cn.stylefeng.roses.kernel.security.guomi.GuomiUtil;
|
||||
import cn.stylefeng.roses.kernel.security.request.encrypt.exception.enums.EncryptionExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.security.request.encrypt.pojo.EncryptionDTO;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 经过解密过的请求体
|
||||
* <p>
|
||||
* 用来将带有加密的原始的HttpInputMessage进行解密,解密后Controller层,正常用自己的body参数接受使用即可
|
||||
* <p>
|
||||
* 从而无感知加密解密过程
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @since 2024/6/28 14:20
|
||||
*/
|
||||
public class CustomDecryptHttpInputMessage implements HttpInputMessage {
|
||||
|
||||
/**
|
||||
* 原始请求的headers
|
||||
*/
|
||||
private final HttpHeaders originalHeaders;
|
||||
|
||||
/**
|
||||
* 原始请求的带加密的body的json字符串
|
||||
*/
|
||||
private final String originalBodyJsonString;
|
||||
|
||||
public CustomDecryptHttpInputMessage(HttpHeaders originalHeaders, String originalBodyJsonString) {
|
||||
this.originalHeaders = originalHeaders;
|
||||
this.originalBodyJsonString = originalBodyJsonString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取解密后的请求体,返回一个ByteArrayInputStream即可
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @since 2024/6/28 14:22
|
||||
*/
|
||||
@Override
|
||||
public InputStream getBody() {
|
||||
|
||||
// 1. 将原始的加密的JSON解析成对象
|
||||
EncryptionDTO encryptionDTO = JSON.parseObject(originalBodyJsonString, EncryptionDTO.class);
|
||||
if (encryptionDTO == null) {
|
||||
throw new ServiceException(EncryptionExceptionEnum.RSA_DECRYPT_ERROR);
|
||||
}
|
||||
if (StrUtil.isBlank(encryptionDTO.getPassedKey()) || StrUtil.isBlank(encryptionDTO.getPassedData())) {
|
||||
throw new ServiceException(EncryptionExceptionEnum.RSA_DECRYPT_ERROR);
|
||||
}
|
||||
|
||||
// 2. 获取到passedKey的字符串,先进行SM2非对称解密,解密后的key是用来解密passedData的
|
||||
String passedKey = encryptionDTO.getPassedKey();
|
||||
String sm4Key = GuomiUtil.sm2DecryptWithPrivate(passedKey);
|
||||
|
||||
// 3. 解密passedData
|
||||
String passedData = encryptionDTO.getPassedData();
|
||||
SM4 sm4 = SmUtil.sm4(sm4Key.getBytes());
|
||||
|
||||
// 4. 获取到解密后的真实的json数据,这个json数据可以直接在Controller层的参数中接收
|
||||
String decryptAfterData = sm4.decryptStr(passedData, StandardCharsets.UTF_8);
|
||||
|
||||
// 5. 将解密后的json数据转换成流返回
|
||||
return new ByteArrayInputStream(decryptAfterData.getBytes(CharsetUtil.CHARSET_UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始的Header参数请求参数,保持不变即可
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @since 2024/6/28 14:22
|
||||
*/
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return originalHeaders;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package cn.stylefeng.roses.kernel.security.request.encrypt.request;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.stylefeng.roses.kernel.scanner.api.annotation.PostResource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.MethodParameter;
|
||||
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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* 请求参数解密
|
||||
*
|
||||
* @author luojie
|
||||
* @since 2021/3/23 12:53
|
||||
*/
|
||||
@Slf4j
|
||||
@ControllerAdvice
|
||||
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {
|
||||
|
||||
/**
|
||||
* 更新前置条件,判断必须是PostResource注解并且requiredEncryption为true
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @since 2024/6/28 14:07
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
PostResource postResource = methodParameter.getAnnotatedElement().getAnnotation(PostResource.class);
|
||||
if (postResource == null) {
|
||||
return false;
|
||||
}
|
||||
return postResource.requiredEncryption();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在读取RequestBody之前,对请求的内容进行解密
|
||||
* <p>
|
||||
* 密文如下:
|
||||
* <pre>
|
||||
* {
|
||||
* "passedKey":"xxxx",
|
||||
* "passedData":"xxxx"
|
||||
* }
|
||||
* </pre>
|
||||
* passedKey是经过SM2非对称加密过的秘钥,这个秘钥用来作为SM4对称加密的秘钥,如果开启了加密,每次passedKey都会随机生成
|
||||
* passedData是将原始参数数据,经过SM4对称加密过的,对称加密的秘钥,需要将passedKey进行SM2非对称加密解密即可
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @since 2024/6/28 14:07
|
||||
*/
|
||||
@Override
|
||||
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage,
|
||||
MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
|
||||
Method method = parameter.getMethod();
|
||||
if (method == null) {
|
||||
return inputMessage;
|
||||
}
|
||||
|
||||
// 获取方法上的PostResource注解
|
||||
PostResource postResource = method.getAnnotation(PostResource.class);
|
||||
if (postResource == null) {
|
||||
return inputMessage;
|
||||
}
|
||||
|
||||
// 将原始的请求body解析,解析为JSON字符串
|
||||
InputStream body = inputMessage.getBody();
|
||||
String encryptedJsonString = IoUtil.readUtf8(body);
|
||||
|
||||
// 请求体为空,原样返回
|
||||
if (StrUtil.isBlank(encryptedJsonString)) {
|
||||
return inputMessage;
|
||||
}
|
||||
|
||||
// 进行解密操作,解密出来HttpInputMessage
|
||||
return new CustomDecryptHttpInputMessage(inputMessage.getHeaders(), encryptedJsonString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object afterBodyRead(Object body,
|
||||
HttpInputMessage inputMessage,
|
||||
MethodParameter parameter,
|
||||
Type targetType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleEmptyBody(Object body,
|
||||
HttpInputMessage inputMessage,
|
||||
MethodParameter parameter,
|
||||
Type targetType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return body;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
@NonNullApi
|
||||
package cn.stylefeng.roses.kernel.security.request.encrypt.request;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
|
@ -1,4 +1,4 @@
|
|||
package cn.stylefeng.roses.kernel.security.request.encrypt.advice;
|
||||
package cn.stylefeng.roses.kernel.security.request.encrypt.response;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
|
@ -12,7 +12,6 @@ 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;
|
||||
|
@ -20,7 +19,6 @@ 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;
|
||||
|
||||
/**
|
||||
|
@ -34,14 +32,6 @@ import java.util.Date;
|
|||
@SuppressWarnings("all")
|
||||
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;
|
Loading…
Reference in New Issue