数据安全问题就不过多讲了,接下来直接讲讲我们咋做的。
前端:请求数据加密、响应数据解密
后端:响应数据加密,请求数据解密
思路:
①静态密钥:前后端提前约定好密钥和算法,请求和响应均进行对称加密。
②动态密钥:约定好加解密算法,前端在页面初始化时请求客户端标识(比如UUID)和动态密钥(可以是对称加密的动态密钥,也可以是非对称加密的动态公钥),后续请求头中均携带客户端标识,后端通过UUID识别客户端,进行请求数据解密和响应数据加密。
很显然,动态密钥要复杂些,但是更安全。
然鹅,我们选择了第一种。因为要快速开发功能应标的缘故。
额是后端人员,只有后端实现。用 RequestBodyAdviceAdapter 和 ResponseBodyAdvice来搞
===================== 后端代码实现 =========================
响应加密:
@Slf4j
@RestControllerAdvice(basePackages = {"com.secure"})
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object>, PriorityOrdered {
@Resource
private SecurityProperties securityProperties;
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
if (returnType.getMethod() == null) {
return false;
}
return securityProperties.getEncryptEnable();
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
try {
HttpServletResponse httpServletResponse = ((ServletServerHttpResponse) response).getServletResponse();
// 设置加密请求头
httpServletResponse.setHeader("X-CRYPT-FOR", "1");
// 加密
String secret = SecureUtil.md5().digestHex16("i am handsome boy!");
SM4 sm4 = SmUtil.sm4(secret.getBytes(StandardCharsets.UTF_8));
return sm4.encryptBase64(JsonUtils.toJsonString(body));
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return body;
}
@Override
public int getOrder() {
return Integer.MAX_VALUE;
}
}
请求解密:
@Slf4j
@RestControllerAdvice
public class DecryptRequestBodyAdvice extends RequestBodyAdviceAdapter {
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
HttpHeaders headers = inputMessage.getHeaders();
String flag = headers.getFirst("X-CRYPT-FOR");
if (!Objects.equals(flag, "1")) {
return inputMessage;
}
byte[] body = IoUtil.readBytes(inputMessage.getBody(), false);
String bodyStr = StrUtil.utf8Str(body);
try {
String secret = SecureUtil.md5().digestHex16("I am handsome boy!");
SM4 sm4 = SmUtil.sm4(secret.getBytes(StandardCharsets.UTF_8));
byte[] decrypt = sm4.decrypt(bodyStr);
final ByteArrayInputStream bais = new ByteArrayInputStream(decrypt);
return new HttpInputMessage() {
@Override
public InputStream getBody() throws IOException {
return bais;
}
@Override
public HttpHeaders getHeaders() {
return inputMessage.getHeaders();
}
};
} catch (Exception e) {
log.error(e.getLocalizedMessage(), e);
}
return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);
}
}