EncryptResponseBodyAdvice.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package cn.licoy.encryptbody.advice;
  2. import cn.licoy.encryptbody.annotation.encrypt.*;
  3. import cn.licoy.encryptbody.bean.EncryptAnnotationInfoBean;
  4. import cn.licoy.encryptbody.enums.EncryptBodyMethod;
  5. import cn.licoy.encryptbody.enums.SHAEncryptType;
  6. import cn.licoy.encryptbody.exception.EncryptBodyFailException;
  7. import cn.licoy.encryptbody.exception.EncryptMethodNotFoundException;
  8. import cn.licoy.encryptbody.util.*;
  9. import com.fasterxml.jackson.core.JsonProcessingException;
  10. import com.fasterxml.jackson.databind.ObjectMapper;
  11. import cn.licoy.encryptbody.config.EncryptBodyConfig;
  12. import jodd.util.ReflectUtil;
  13. import lombok.SneakyThrows;
  14. import lombok.extern.slf4j.Slf4j;
  15. import org.springframework.beans.factory.annotation.Autowired;
  16. import org.springframework.beans.factory.annotation.Value;
  17. import org.springframework.core.MethodParameter;
  18. import org.springframework.core.annotation.Order;
  19. import org.springframework.http.MediaType;
  20. import org.springframework.http.server.ServerHttpRequest;
  21. import org.springframework.http.server.ServerHttpResponse;
  22. import org.springframework.web.bind.annotation.ControllerAdvice;
  23. import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
  24. import java.lang.annotation.Annotation;
  25. import java.util.ArrayList;
  26. import java.util.Arrays;
  27. import java.util.List;
  28. import java.util.stream.Collectors;
  29. /**
  30. * 响应数据的加密处理<br>
  31. * 本类只对控制器参数中含有<strong>{@link org.springframework.web.bind.annotation.ResponseBody}</strong>
  32. * 或者控制类上含有<strong>{@link org.springframework.web.bind.annotation.RestController}</strong>
  33. * 以及package为<strong><code>cn.licoy.encryptbody.annotation.encrypt</code></strong>下的注解有效
  34. *
  35. * @author licoy.cn
  36. * @version 2018/9/4
  37. * @see ResponseBodyAdvice
  38. */
  39. @Order(1)
  40. @ControllerAdvice
  41. @Slf4j
  42. public class EncryptResponseBodyAdvice implements ResponseBodyAdvice {
  43. private final ObjectMapper objectMapper;
  44. private final EncryptBodyConfig config;
  45. @Value("${encrypt.enable}")
  46. private boolean enableEncrypt;
  47. @Autowired
  48. public EncryptResponseBodyAdvice(ObjectMapper objectMapper, EncryptBodyConfig config) {
  49. this.objectMapper = objectMapper;
  50. this.config = config;
  51. }
  52. @Override
  53. public boolean supports(MethodParameter returnType, Class converterType) {
  54. if (!enableEncrypt) return false;
  55. Annotation[] annotations = returnType.getDeclaringClass().getAnnotations();
  56. if (annotations != null && annotations.length > 0) {
  57. for (Annotation annotation : annotations) {
  58. if (annotation instanceof EncryptBody ||
  59. annotation instanceof AESEncryptBody ||
  60. annotation instanceof DESEncryptBody ||
  61. annotation instanceof RSAEncryptBody ||
  62. annotation instanceof MD5EncryptBody ||
  63. annotation instanceof SHAEncryptBody) {
  64. return true;
  65. }
  66. }
  67. }
  68. if (Arrays.stream(ReflectUtil.getSuperclasses(returnType.getDeclaringClass()))
  69. .flatMap(clazz -> Arrays.stream(clazz.getAnnotations()))
  70. .anyMatch(annotation -> annotation instanceof EncryptBody ||
  71. annotation instanceof AESEncryptBody ||
  72. annotation instanceof DESEncryptBody ||
  73. annotation instanceof RSAEncryptBody ||
  74. annotation instanceof MD5EncryptBody ||
  75. annotation instanceof SHAEncryptBody)) {
  76. return true;
  77. }
  78. return returnType.getMethod().isAnnotationPresent(EncryptBody.class) ||
  79. returnType.getMethod().isAnnotationPresent(AESEncryptBody.class) ||
  80. returnType.getMethod().isAnnotationPresent(DESEncryptBody.class) ||
  81. returnType.getMethod().isAnnotationPresent(RSAEncryptBody.class) ||
  82. returnType.getMethod().isAnnotationPresent(MD5EncryptBody.class) ||
  83. returnType.getMethod().isAnnotationPresent(SHAEncryptBody.class);
  84. }
  85. @SneakyThrows
  86. @Override
  87. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
  88. Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
  89. if (body == null) return null;
  90. response.getHeaders().add("Content-Encrypted", "true");
  91. String str = null;
  92. try {
  93. str = objectMapper.writeValueAsString(body);
  94. } catch (JsonProcessingException e) {
  95. e.printStackTrace();
  96. }
  97. EncryptAnnotationInfoBean classAnnotation = getClassAnnotation(returnType.getDeclaringClass());
  98. if (classAnnotation != null) {
  99. return switchEncrypt(str, classAnnotation);
  100. }
  101. EncryptAnnotationInfoBean methodAnnotation = getMethodAnnotation(returnType);
  102. if (methodAnnotation != null) {
  103. return switchEncrypt(str, methodAnnotation);
  104. }
  105. throw new EncryptBodyFailException();
  106. }
  107. /**
  108. * 获取方法控制器上的加密注解信息
  109. *
  110. * @param methodParameter 控制器方法
  111. * @return 加密注解信息
  112. */
  113. private EncryptAnnotationInfoBean getMethodAnnotation(MethodParameter methodParameter) {
  114. if (methodParameter.getMethod().isAnnotationPresent(EncryptBody.class)) {
  115. EncryptBody encryptBody = methodParameter.getMethodAnnotation(EncryptBody.class);
  116. return EncryptAnnotationInfoBean.builder()
  117. .encryptBodyMethod(encryptBody.value())
  118. .key(encryptBody.otherKey())
  119. .shaEncryptType(encryptBody.shaType())
  120. .build();
  121. }
  122. if (methodParameter.getMethod().isAnnotationPresent(MD5EncryptBody.class)) {
  123. return EncryptAnnotationInfoBean.builder()
  124. .encryptBodyMethod(EncryptBodyMethod.MD5)
  125. .build();
  126. }
  127. if (methodParameter.getMethod().isAnnotationPresent(SHAEncryptBody.class)) {
  128. return EncryptAnnotationInfoBean.builder()
  129. .encryptBodyMethod(EncryptBodyMethod.SHA)
  130. .shaEncryptType(methodParameter.getMethodAnnotation(SHAEncryptBody.class).value())
  131. .build();
  132. }
  133. if (methodParameter.getMethod().isAnnotationPresent(DESEncryptBody.class)) {
  134. return EncryptAnnotationInfoBean.builder()
  135. .encryptBodyMethod(EncryptBodyMethod.DES)
  136. .key(methodParameter.getMethodAnnotation(DESEncryptBody.class).otherKey())
  137. .build();
  138. }
  139. if (methodParameter.getMethod().isAnnotationPresent(AESEncryptBody.class)) {
  140. return EncryptAnnotationInfoBean.builder()
  141. .encryptBodyMethod(EncryptBodyMethod.AES)
  142. .key(methodParameter.getMethodAnnotation(AESEncryptBody.class).otherKey())
  143. .build();
  144. }
  145. return null;
  146. }
  147. /**
  148. * 获取类控制器上的加密注解信息
  149. *
  150. * @param clazz 控制器类
  151. * @return 加密注解信息
  152. */
  153. private EncryptAnnotationInfoBean getClassAnnotation(Class clazz) {
  154. List<Annotation> annotations = new ArrayList<>(Arrays.asList(clazz.getSuperclass().getDeclaredAnnotations()));
  155. annotations.addAll(Arrays.asList(clazz.getDeclaredAnnotations()));
  156. for (Annotation annotation : annotations) {
  157. if (annotation instanceof EncryptBody) {
  158. EncryptBody encryptBody = (EncryptBody) annotation;
  159. return EncryptAnnotationInfoBean.builder()
  160. .encryptBodyMethod(encryptBody.value())
  161. .key(encryptBody.otherKey())
  162. .shaEncryptType(encryptBody.shaType())
  163. .build();
  164. }
  165. if (annotation instanceof MD5EncryptBody) {
  166. return EncryptAnnotationInfoBean.builder()
  167. .encryptBodyMethod(EncryptBodyMethod.MD5)
  168. .build();
  169. }
  170. if (annotation instanceof SHAEncryptBody) {
  171. return EncryptAnnotationInfoBean.builder()
  172. .encryptBodyMethod(EncryptBodyMethod.SHA)
  173. .shaEncryptType(((SHAEncryptBody) annotation).value())
  174. .build();
  175. }
  176. if (annotation instanceof DESEncryptBody) {
  177. return EncryptAnnotationInfoBean.builder()
  178. .encryptBodyMethod(EncryptBodyMethod.DES)
  179. .key(((DESEncryptBody) annotation).otherKey())
  180. .build();
  181. }
  182. if (annotation instanceof AESEncryptBody) {
  183. return EncryptAnnotationInfoBean.builder()
  184. .encryptBodyMethod(EncryptBodyMethod.AES)
  185. .key(((AESEncryptBody) annotation).otherKey())
  186. .build();
  187. }
  188. }
  189. return null;
  190. }
  191. /**
  192. * 选择加密方式并进行加密
  193. *
  194. * @param formatStringBody 目标加密字符串
  195. * @param infoBean 加密信息
  196. * @return 加密结果
  197. */
  198. private String switchEncrypt(String formatStringBody, EncryptAnnotationInfoBean infoBean) throws Exception {
  199. EncryptBodyMethod method = infoBean.getEncryptBodyMethod();
  200. if (method == null) {
  201. throw new EncryptMethodNotFoundException();
  202. }
  203. if (method == EncryptBodyMethod.MD5) {
  204. return MD5EncryptUtil.encrypt(formatStringBody);
  205. }
  206. if (method == EncryptBodyMethod.SHA) {
  207. SHAEncryptType shaEncryptType = infoBean.getShaEncryptType();
  208. if (shaEncryptType == null) shaEncryptType = SHAEncryptType.SHA256;
  209. return SHAEncryptUtil.encrypt(formatStringBody, shaEncryptType);
  210. }
  211. String key = infoBean.getKey();
  212. if (method == EncryptBodyMethod.DES) {
  213. key = CheckUtils.checkAndGetKey(config.getDesKey(), key, "DES-KEY");
  214. return DESEncryptUtil.encrypt(formatStringBody, key);
  215. }
  216. if (method == EncryptBodyMethod.AES) {
  217. key = CheckUtils.checkAndGetKey(config.getAesKey(), key, "AES-KEY");
  218. return AESEncryptUtil.encrypt(formatStringBody, key);
  219. }
  220. throw new EncryptBodyFailException();
  221. }
  222. }