package cn.licoy.encryptbody.advice; import cn.licoy.encryptbody.annotation.encrypt.*; import cn.licoy.encryptbody.bean.EncryptAnnotationInfoBean; import cn.licoy.encryptbody.enums.EncryptBodyMethod; import cn.licoy.encryptbody.enums.SHAEncryptType; import cn.licoy.encryptbody.exception.EncryptBodyFailException; import cn.licoy.encryptbody.exception.EncryptMethodNotFoundException; import cn.licoy.encryptbody.util.*; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import cn.licoy.encryptbody.config.EncryptBodyConfig; import jodd.util.ReflectUtil; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.Order; 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.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * 响应数据的加密处理
* 本类只对控制器参数中含有{@link org.springframework.web.bind.annotation.ResponseBody} * 或者控制类上含有{@link org.springframework.web.bind.annotation.RestController} * 以及package为cn.licoy.encryptbody.annotation.encrypt下的注解有效 * * @author licoy.cn * @version 2018/9/4 * @see ResponseBodyAdvice */ @Order(1) @ControllerAdvice @Slf4j public class EncryptResponseBodyAdvice implements ResponseBodyAdvice { private final ObjectMapper objectMapper; private final EncryptBodyConfig config; @Value("${encrypt.enable}") private boolean enableEncrypt; @Autowired public EncryptResponseBodyAdvice(ObjectMapper objectMapper, EncryptBodyConfig config) { this.objectMapper = objectMapper; this.config = config; } @Override public boolean supports(MethodParameter returnType, Class converterType) { if (!enableEncrypt) return false; Annotation[] annotations = returnType.getDeclaringClass().getAnnotations(); if (annotations != null && annotations.length > 0) { for (Annotation annotation : annotations) { if (annotation instanceof EncryptBody || annotation instanceof AESEncryptBody || annotation instanceof DESEncryptBody || annotation instanceof RSAEncryptBody || annotation instanceof MD5EncryptBody || annotation instanceof SHAEncryptBody) { return true; } } } if (Arrays.stream(ReflectUtil.getSuperclasses(returnType.getDeclaringClass())) .flatMap(clazz -> Arrays.stream(clazz.getAnnotations())) .anyMatch(annotation -> annotation instanceof EncryptBody || annotation instanceof AESEncryptBody || annotation instanceof DESEncryptBody || annotation instanceof RSAEncryptBody || annotation instanceof MD5EncryptBody || annotation instanceof SHAEncryptBody)) { return true; } return returnType.getMethod().isAnnotationPresent(EncryptBody.class) || returnType.getMethod().isAnnotationPresent(AESEncryptBody.class) || returnType.getMethod().isAnnotationPresent(DESEncryptBody.class) || returnType.getMethod().isAnnotationPresent(RSAEncryptBody.class) || returnType.getMethod().isAnnotationPresent(MD5EncryptBody.class) || returnType.getMethod().isAnnotationPresent(SHAEncryptBody.class); } @SneakyThrows @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (body == null) return null; response.getHeaders().add("Content-Encrypted", "true"); String str = null; try { str = objectMapper.writeValueAsString(body); } catch (JsonProcessingException e) { e.printStackTrace(); } EncryptAnnotationInfoBean classAnnotation = getClassAnnotation(returnType.getDeclaringClass()); if (classAnnotation != null) { return switchEncrypt(str, classAnnotation); } EncryptAnnotationInfoBean methodAnnotation = getMethodAnnotation(returnType); if (methodAnnotation != null) { return switchEncrypt(str, methodAnnotation); } throw new EncryptBodyFailException(); } /** * 获取方法控制器上的加密注解信息 * * @param methodParameter 控制器方法 * @return 加密注解信息 */ private EncryptAnnotationInfoBean getMethodAnnotation(MethodParameter methodParameter) { if (methodParameter.getMethod().isAnnotationPresent(EncryptBody.class)) { EncryptBody encryptBody = methodParameter.getMethodAnnotation(EncryptBody.class); return EncryptAnnotationInfoBean.builder() .encryptBodyMethod(encryptBody.value()) .key(encryptBody.otherKey()) .shaEncryptType(encryptBody.shaType()) .build(); } if (methodParameter.getMethod().isAnnotationPresent(MD5EncryptBody.class)) { return EncryptAnnotationInfoBean.builder() .encryptBodyMethod(EncryptBodyMethod.MD5) .build(); } if (methodParameter.getMethod().isAnnotationPresent(SHAEncryptBody.class)) { return EncryptAnnotationInfoBean.builder() .encryptBodyMethod(EncryptBodyMethod.SHA) .shaEncryptType(methodParameter.getMethodAnnotation(SHAEncryptBody.class).value()) .build(); } if (methodParameter.getMethod().isAnnotationPresent(DESEncryptBody.class)) { return EncryptAnnotationInfoBean.builder() .encryptBodyMethod(EncryptBodyMethod.DES) .key(methodParameter.getMethodAnnotation(DESEncryptBody.class).otherKey()) .build(); } if (methodParameter.getMethod().isAnnotationPresent(AESEncryptBody.class)) { return EncryptAnnotationInfoBean.builder() .encryptBodyMethod(EncryptBodyMethod.AES) .key(methodParameter.getMethodAnnotation(AESEncryptBody.class).otherKey()) .build(); } return null; } /** * 获取类控制器上的加密注解信息 * * @param clazz 控制器类 * @return 加密注解信息 */ private EncryptAnnotationInfoBean getClassAnnotation(Class clazz) { List annotations = new ArrayList<>(Arrays.asList(clazz.getSuperclass().getDeclaredAnnotations())); annotations.addAll(Arrays.asList(clazz.getDeclaredAnnotations())); for (Annotation annotation : annotations) { if (annotation instanceof EncryptBody) { EncryptBody encryptBody = (EncryptBody) annotation; return EncryptAnnotationInfoBean.builder() .encryptBodyMethod(encryptBody.value()) .key(encryptBody.otherKey()) .shaEncryptType(encryptBody.shaType()) .build(); } if (annotation instanceof MD5EncryptBody) { return EncryptAnnotationInfoBean.builder() .encryptBodyMethod(EncryptBodyMethod.MD5) .build(); } if (annotation instanceof SHAEncryptBody) { return EncryptAnnotationInfoBean.builder() .encryptBodyMethod(EncryptBodyMethod.SHA) .shaEncryptType(((SHAEncryptBody) annotation).value()) .build(); } if (annotation instanceof DESEncryptBody) { return EncryptAnnotationInfoBean.builder() .encryptBodyMethod(EncryptBodyMethod.DES) .key(((DESEncryptBody) annotation).otherKey()) .build(); } if (annotation instanceof AESEncryptBody) { return EncryptAnnotationInfoBean.builder() .encryptBodyMethod(EncryptBodyMethod.AES) .key(((AESEncryptBody) annotation).otherKey()) .build(); } } return null; } /** * 选择加密方式并进行加密 * * @param formatStringBody 目标加密字符串 * @param infoBean 加密信息 * @return 加密结果 */ private String switchEncrypt(String formatStringBody, EncryptAnnotationInfoBean infoBean) throws Exception { EncryptBodyMethod method = infoBean.getEncryptBodyMethod(); if (method == null) { throw new EncryptMethodNotFoundException(); } if (method == EncryptBodyMethod.MD5) { return MD5EncryptUtil.encrypt(formatStringBody); } if (method == EncryptBodyMethod.SHA) { SHAEncryptType shaEncryptType = infoBean.getShaEncryptType(); if (shaEncryptType == null) shaEncryptType = SHAEncryptType.SHA256; return SHAEncryptUtil.encrypt(formatStringBody, shaEncryptType); } String key = infoBean.getKey(); if (method == EncryptBodyMethod.DES) { key = CheckUtils.checkAndGetKey(config.getDesKey(), key, "DES-KEY"); return DESEncryptUtil.encrypt(formatStringBody, key); } if (method == EncryptBodyMethod.AES) { key = CheckUtils.checkAndGetKey(config.getAesKey(), key, "AES-KEY"); return AESEncryptUtil.encrypt(formatStringBody, key); } throw new EncryptBodyFailException(); } }