0


前后端分离数据传输加解密方案(建议方案二)

方案一 请求响应参数全部加密

1.优缺点

  1. a.优点:实现简单,比明文传输安全
  2. b.缺点:1)由于加密所有参数,效率低下 2)信息全加密,不利于前后端联调 3)密钥传输不安全,容易被拦截
  3. 优化点:前端生成AES对称加密密钥,用rsa私钥非对称加密将AES密钥加密,传给到后端,后端用rsa公钥解密后获取到AES密钥,这样前后端就有了公共的AES密钥了

2.开发步骤(AES对称加密)

加密流程:

  1. a.前端调用接口/web/security/v1/getAesKey 并将其保存在sesssionStrage
  2. b.后端在前端调用/web/security/v1/getAesKey时,生成AES密钥并保存在session中,并返回给前端
  3. c.前端访问其他接口传入的参数都用此AES密钥加密,接收到的响应数据都用此AES密钥解密,可以拦截器,对所有请求和响应加解密
  4. d.后端和前端一样用此AES密钥进行加解密

前端加解密

  1. <script src="https://cdn.bootcss.com/crypto-js/3.1.9-1/crypto-js.min.js"></script>
  2. /**
  3. * 加密
  4. **/
  5. function encrypt(value,key) {
  6. var tempValue = JSON.stringify(value);
  7. var tempKey = CryptoJS.enc.Utf8.parse(key);
  8. var srcs = CryptoJS.enc.Utf8.parse(tempValue);
  9. var encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB,
  10. padding:CryptoJS.pad.Pkcs7 });
  11. var encryptedValue = encrypted.toString();
  12. return encryptedValue;
  13. }
  14. /**
  15. *解密
  16. **/
  17. function decrypt(value,key) {
  18. var keyStr = CryptoJS.enc.Utf8.parse(key)
  19. var decrypt = CryptoJS.AES.decrypt(value, keyStr, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 })
  20. return CryptoJS.enc.Utf8.stringify(decrypt).toString()
  21. }

b.后端加解密

  1. public class AesEncrypt {
  2. /**
  3. * 加密
  4. *
  5. * @param value数据
  6. * @param key 密钥
  7. * @return 加密后内容
  8. */
  9. public static byte[] encrypt(byte[] value, String key) throw Exception{
  10. Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
  11. cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes("UTF-8"), "AES"));
  12. return cipher.doFinal(value);
  13. }
  14. /**
  15. * 解密
  16. *
  17. * @param value数据
  18. * @param key 密钥
  19. * @return 解密后内容
  20. */
  21. public static byte[] decrypt(byte[] value, String key) throw Exception {
  22. Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
  23. SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
  24. //使用密钥初始化,设置为解密模式
  25. cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
  26. //执行操作
  27. return cipher.doFinal(value);
  28. }
  29. }

c.后端拦截代码实现

  1. /**
  2. * 请求过滤器,记得注册
  3. **/
  4. public EncryptFilter implements Filter {
  5. @Override
  6. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
  7. throws IOException, ServletException {
  8. HttpServletRequest request = (HttpServletRequest) servletRequest;
  9. String uri = request.getRequestURI();
  10. LOGGER.debug("进入加解密过滤器,URI:{}", uri);
  11. HttpServletResponse response = (HttpServletResponse) servletResponse;
  12. AesHttpServletRequestWrapper aesHttpServletRequestWrapper = new AesHttpServletRequestWrapper(request);
  13. filterChain.doFilter(aesHttpServletRequestWrapper, response);
  14. }
  15. }
  1. /**
  2. * (1)请求拦截,解密
  3. **/
  4. public class AesHttpServletRequestWrapper extends HttpServletRequestWrapper {
  5. private String bodyContent;
  6. private Parameters parameters = new Parameters();
  7. private HttpServletRequest request;
  8. public AesHttpServletRequestWrapper (HttpServletRequest request) {
  9. request = request;
  10. initWrapper();
  11. }
  12. private void initWrapper() {
  13. this.parameters.setCharset(charset);
  14. readBodyBytes();
  15. this.parseParameterMap();
  16. }
  17. private void readBodyBytes() {
  18. if (this.bodyContent == null) {
  19. try {
  20. byte[] bodyBytes = readInputBody(request.getInputStream());
  21. this.bodyContent = new String(AesEncrypt.decrypt(bodyBytes), charset);
  22. } catch (IOException e) {
  23. throw new RuntimeException(e);
  24. }
  25. }
  26. }
  27. private byte[] readInputBody(InputStream inputStream) throws IOException {
  28. ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  29. byte[] buffer = new byte[1024];
  30. int len;
  31. while ((len = inputStream.read(buffer)) > -1) {
  32. byteArrayOutputStream.write(buffer, 0, len);
  33. }
  34. byteArrayOutputStream.flush();
  35. return byteArrayOutputStream.toByteArray();
  36. }
  37. @Override
  38. public String getQueryString() {
  39. return queryString;
  40. }
  41. @Override
  42. public String getParameter(String name) {
  43. String[] values = getParameterValues(name);
  44. return values == null || values.length == 0 ? null : values[0];
  45. }
  46. @Override
  47. public int getContentLength() {
  48. return bodyContent.getBytes(charset).length;
  49. }
  50. @Override
  51. public long getContentLengthLong() {
  52. return bodyContent.getBytes(charset).length;
  53. }
  54. @Override
  55. public Map<String, String[]> getParameterMap() {
  56. if (paramsMap == null) {
  57. paramsMap = new HashMap<>();
  58. Enumeration<String> nameEnum = this.parameters.getParameterNames();
  59. while (nameEnum.hasMoreElements()) {
  60. String name = nameEnum.nextElement();
  61. paramsMap.put(name, getParameterValues(name));
  62. }
  63. }
  64. return paramsMap;
  65. }
  66. @Override
  67. public Enumeration<String> getParameterNames() {
  68. return this.parameters.getParameterNames();
  69. }
  70. @Override
  71. public String[] getParameterValues(String name) {
  72. return parameters.getParameterValues(name);
  73. }
  74. }
  1. /**
  2. * (2) 响应拦截 加密
  3. **/
  4. @ControllerAdvice
  5. public class AesResponseAdvice implements ResponseBodyAdvice<Object> {
  6. private static final String AES_KEY= "AES_KEY";
  7. @Override
  8. public Object beforeBodyWrite(Object body, MethodParameter methodParameter,
  9. MediaType mediaType,
  10. Class<? extends HttpMessageConverter<?>> converterClass,
  11. ServerHttpRequest serverHttpRequest,
  12. ServerHttpResponse serverHttpResponse) {
  13. ServletServerHttpRequest request = (ServletServerHttpRequest) serverHttpRequest;
  14. if (body != null) {
  15. ServletServerHttpResponse response = (ServletServerHttpResponse) serverHttpResponse;
  16. try {
  17. byte[] contentBytes = null;
  18. contentBytes = String.valueOf(body).getBytes(Charset.forName("UTF-8"));
  19. Object aesKey = request.getServletRequest().getSession().getAttribute(AES_KEY);
  20. body = AesEncrypt.encrypt(contentBytes,String.value(aesKey));
  21. } catch (IOException e) {
  22. LOGGER.error("加密数据失败", e);
  23. }
  24. }
  25. return body;
  26. }
  27. }

方案一 请求响应参数自定义加密字段(比如id)

1.优缺点

  1. a.优点:效率高,只加密某些重要字段
  2. b.缺点:实现复杂

2.开发步骤

a.自定义注解 @Encrypt,放属性上或者参数上

  1. @JacksonAnnotation
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target({ElementType.FIELD, ElementType.PARAMETER})
  4. public @interface Encrypt {
  5. String value() default "";
  6. String fieldName() default "";
  7. String[] ignoreValue() default {};
  8. }

b.springmvc中增加请求参数解析器,响应参数解析器

  1. public class EncryptRequestParamMethodArgumentResolver extends RequestParamMethodArgumentResolver {
  2. IEncryptionService encryptionService;
  3. @Override
  4. @Nullable
  5. protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
  6. Object result = super.resolveName(name, parameter, request);
  7. if (EncryptContext.isEncrypt() && result != null && parameter.hasParameterAnnotation(Encrypt.class)) {
  8. Encrypt encrypt = parameter.getParameterAnnotation(Encrypt.class);
  9. if (result instanceof String && !EncryptUtils.ignoreValue(encrypt, (String) result)) {
  10. if (isArray(parameter)) {
  11. result = ((String) result).split(",");
  12. } else {
  13. result = encryptionService.decrypt(((String) result), encrypt.value());
  14. return result;
  15. }
  16. }
  17. if (result instanceof String[]) {
  18. String[] oldResult = (String[]) result;
  19. String[] newResult = new String[oldResult.length];
  20. for (int i = 0; i < oldResult.length; i++) {
  21. if (EncryptUtils.ignoreValue(encrypt, oldResult[i])) {
  22. newResult[i] = oldResult[i];
  23. } else {
  24. newResult[i] = encryptionService.decrypt(oldResult[i], encrypt.value());
  25. }
  26. }
  27. return newResult;
  28. }
  29. }
  30. return result;
  31. }
  1. public class EncryptRequestResponseBodyMethodProcessor extends RequestResponseBodyMethodProcessor {
  2. @Override
  3. public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
  4. NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  5. if (!EncryptContext.isEncrypt()) {
  6. return super.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
  7. }
  8. Encrypt encrypt = parameter.getParameterAnnotation(Encrypt.class);
  9. if (encrypt == null) {
  10. return super.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
  11. }
  12. // 如果是集合类,且范型类型是基础类型的包装类型
  13. if (Collection.class.isAssignableFrom(parameter.getParameterType())) {
  14. return resolveCollectionArgument(parameter, mavContainer, webRequest, binderFactory, encrypt);
  15. }
  16. // 如果是集合类,且范型类型是基础类型的包装类型
  17. if (parameter.getParameterType().isArray()) {
  18. return resolveArrayArgument(parameter, mavContainer, webRequest, binderFactory, encrypt);
  19. }
  20. // 如果是 Map 类,且泛型类型是基础类型顶的包装类型
  21. if (Map.class.isAssignableFrom(parameter.getParameterType())) {
  22. return resolveMapArgument(parameter, mavContainer, webRequest, binderFactory, encrypt);
  23. }
  24. return super.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
  25. }
  26. }

c.将请求,响应参数解析器,加入到springmvc解析器列表中HandlerMethodArgumentResolver

  1. public class WebBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
  2. @Nullable
  3. @Override
  4. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  5. if (bean instanceof RequestMappingHandlerAdapter) {
  6. RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
  7. List<HandlerMethodArgumentResolver> currentResolvers = adapter.getArgumentResolvers();
  8. if (currentResolvers == null) {
  9. throw new IllegalStateException(
  10. String.format("No HandlerMethodArgumentResolvers found in RequestMappingHandlerAdapter %s!", beanName));
  11. }
  12. //IEncryptionService encryptionService = new EncryptionService();
  13. //替换PathVariableMethodArgumentResolver 和 RequestParamMethodArgumentResolver
  14. PathVariableMethodArgumentResolver pathVariableMethodArgumentResolver = new EncryptPathVariableMethodArgumentResolver(encryptionService);
  15. RequestParamMethodArgumentResolver requestParamMethodArgumentResolverFalse = new EncryptRequestParamMethodArgumentResolver(encryptionService, beanFactory, false);
  16. RequestParamMethodArgumentResolver requestParamMethodArgumentResolverTrue = new EncryptRequestParamMethodArgumentResolver(encryptionService, beanFactory, true);
  17. EncryptRequestResponseBodyMethodProcessor encryptRequestResponseBodyMethodProcessor;
  18. try {
  19. encryptRequestResponseBodyMethodProcessor = new EncryptRequestResponseBodyMethodProcessor(adapter.getMessageConverters(),
  20. (List<Object>) FieldUtils.readDeclaredField(adapter, "requestResponseBodyAdvice", true));
  21. } catch (IllegalAccessException e) {
  22. throw new CommonException(e);
  23. }
  24. encryptRequestResponseBodyMethodProcessor.setEncryptionService(encryptionService);
  25. List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(adapter.getArgumentResolvers().size());
  26. //spring 默认注册了2个requestParamMethodArgumentResolver
  27. boolean isFirst = true;
  28. for (HandlerMethodArgumentResolver resolver : adapter.getArgumentResolvers()) {
  29. if (resolver instanceof PathVariableMethodArgumentResolver) {
  30. resolvers.add(pathVariableMethodArgumentResolver);
  31. continue;
  32. }
  33. if (resolver instanceof RequestParamMethodArgumentResolver) {
  34. if (isFirst) {
  35. resolvers.add(requestParamMethodArgumentResolverFalse);
  36. isFirst = false;
  37. continue;
  38. }
  39. resolvers.add(requestParamMethodArgumentResolverTrue);
  40. continue;
  41. }
  42. if (resolver instanceof RequestResponseBodyMethodProcessor) {
  43. resolvers.add(encryptRequestResponseBodyMethodProcessor);
  44. continue;
  45. }
  46. resolvers.add(resolver);
  47. }
  48. adapter.setArgumentResolvers(resolvers);
  49. }

3.实现效果


本文转载自: https://blog.csdn.net/zengliangxi/article/details/127067680
版权归原作者 zengliangxi 所有, 如有侵权,请联系我们删除。

“前后端分离数据传输加解密方案(建议方案二)”的评论:

还没有评论