【8.1.8】【security】更新相应数据加密的过程

dev-8.1.9
stylefeng 2024-06-28 15:29:32 +08:00
parent d2c0b5b9b1
commit b1672adcdb
8 changed files with 155 additions and 143 deletions

View File

@ -14,20 +14,15 @@ import lombok.Getter;
@Getter
public enum EncryptionExceptionEnum implements AbstractExceptionEnum {
/**
* json
*/
REQUEST_JSON_PARSE_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + EncryptionConstants.ENCRYPTION_EXCEPTION_STEP_CODE + "01", "请求的json解析异常"),
/**
* jsondatakey
*/
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 + "01", "请求解密失败,原始数据格式错误"),
/**
*
*/
GET_SM4_KEY_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + EncryptionConstants.ENCRYPTION_EXCEPTION_STEP_CODE + "02", "响应数据时获取秘钥失败,无法加密");
/**
*

View File

@ -14,7 +14,7 @@ public class EncryptRemoveThreadLocalHolder implements RemoveThreadLocalApi {
@Override
public void removeThreadLocalAction() {
EncryptionHolder.clearAesKey();
TempSm4KeyHolder.clearSm4Key();
}
}

View File

@ -1,43 +0,0 @@
package cn.stylefeng.roses.kernel.security.request.encrypt.holder;
/**
*
*
* @author luojie
* @since 2021/3/23 12:54
*/
public class EncryptionHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
*
*
* @param aesKey aesKey
* @since 2020/8/24
*/
public static void setAesKey(String aesKey) {
CONTEXT_HOLDER.set(aesKey);
}
/**
*
*
* @author fengshuonan
* @since 2020/8/24
*/
public static String getAesKey() {
return CONTEXT_HOLDER.get();
}
/**
*
*
* @author fengshuonan
* @since 2020/8/24
*/
public static void clearAesKey() {
CONTEXT_HOLDER.remove();
}
}

View File

@ -0,0 +1,43 @@
package cn.stylefeng.roses.kernel.security.request.encrypt.holder;
/**
* SM4
*
* @author fengshuonan
* @since 2024/6/28 15:13
*/
public class TempSm4KeyHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* SM4
*
* @author fengshuonan
* @since 2024/6/28 15:13
*/
public static void setSm4Key(String sm4Key) {
CONTEXT_HOLDER.set(sm4Key);
}
/**
* SM4
*
* @author fengshuonan
* @since 2024/6/28 15:13
*/
public static String getSm4Key() {
return CONTEXT_HOLDER.get();
}
/**
* SM4
*
* @author fengshuonan
* @since 2020/8/24
*/
public static void clearSm4Key() {
CONTEXT_HOLDER.remove();
}
}

View File

@ -8,6 +8,7 @@ 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.holder.TempSm4KeyHolder;
import cn.stylefeng.roses.kernel.security.request.encrypt.pojo.EncryptionDTO;
import com.alibaba.fastjson.JSON;
import org.springframework.http.HttpHeaders;
@ -66,6 +67,9 @@ public class CustomDecryptHttpInputMessage implements HttpInputMessage {
String passedKey = encryptionDTO.getPassedKey();
String sm4Key = GuomiUtil.sm2DecryptWithPrivate(passedKey);
// 临时缓存Sm4Key响应时候还需要加密
TempSm4KeyHolder.setSm4Key(sm4Key);
// 3. 解密passedData
String passedData = encryptionDTO.getPassedData();
SM4 sm4 = SmUtil.sm4(sm4Key.getBytes());

View File

@ -0,0 +1,97 @@
package cn.stylefeng.roses.kernel.security.request.encrypt.response;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
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.exception.EncryptionException;
import cn.stylefeng.roses.kernel.security.request.encrypt.exception.enums.EncryptionExceptionEnum;
import cn.stylefeng.roses.kernel.security.request.encrypt.holder.TempSm4KeyHolder;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.nio.charset.StandardCharsets;
/**
*
*
* @author fengshuonan
* @since 2024/6/28 14:51
*/
@Slf4j
@ControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {
/**
* PostResourcerequiredEncryptiontrue
*
* @author fengshuonan
* @since 2024/6/28 14:54
*/
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) {
PostResource postResource = methodParameter.getAnnotatedElement().getAnnotation(PostResource.class);
if (postResource == null) {
return false;
}
return postResource.requiredEncryption();
}
@Override
@SuppressWarnings("rawtypes,unchecked")
public Object beforeBodyWrite(@Nullable Object originBody,
MethodParameter methodParameter,
MediaType selectedContentType,
Class selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
// 原始数据为空,直接返回
if (originBody == null) {
return null;
}
// 判断响应实体是否是ResponseData只针对ResponseData进行加密
if (!(originBody instanceof ResponseData)) {
return originBody;
}
// 转换类型获取ResponseData中的具体数据
ResponseData responseData = (ResponseData) originBody;
Object data = responseData.getData();
if (data == null) {
return originBody;
}
// 从ThreadLocal中获取解密出的SM4对称加密的秘钥
String sm4Key = TempSm4KeyHolder.getSm4Key();
if (StrUtil.isBlank(sm4Key)) {
throw new EncryptionException(EncryptionExceptionEnum.GET_SM4_KEY_ERROR);
}
// 原始的Data转化为字符串准备加密
String originJsonString = JSON.toJSONString(data);
// 进行SM4加密
SM4 sm4 = SmUtil.sm4(sm4Key.getBytes());
String encryptBase64 = sm4.encryptBase64(originJsonString, StandardCharsets.UTF_8);
// 将加密后的数据放入ResponseData中
responseData.setData(encryptBase64);
// 清除当前线程中的aes key
TempSm4KeyHolder.clearSm4Key();
return responseData;
}
}

View File

@ -1,88 +0,0 @@
package cn.stylefeng.roses.kernel.security.request.encrypt.response;
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.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.util.Date;
/**
*
*
* @author luojie
* @since 2021/3/23 12:54
*/
@Slf4j
@ControllerAdvice
@SuppressWarnings("all")
public class EncryptionResponseBodyAdvice implements ResponseBodyAdvice {
@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;
}
}

View File

@ -0,0 +1,4 @@
@NonNullApi
package cn.stylefeng.roses.kernel.security.request.encrypt.response;
import org.springframework.lang.NonNullApi;