我们与客户端的接口交互中,为了更高的安全性,我们可能需要对接口加密(请求参数加密,服务端解密)、返回信息加密(服务端加密,客户端解密),但是也不是所有的接口都这样,有些接口可能不需要,我们可以使用注解来轻松达到此要求。
将接口参数的加密解密和返回信息的加密解密分开,分别定义注解,利用Controller的ControllerAdvice来拦截所有的请求,在其中判断是否需要加密解密,即可达到要求。
使用方法:使用 DecryptRequest 和 EncryptResponse 注解即可,可以放在Controller的类和方法上,其中一个为false就不执行了。像这样:
@RestController @RequestMapping("/test") //@DecryptRequest @EncryptResponse public class TestController { @Autowired @Qualifier("rrCrypto") private Crypto crypto; @DecryptRequest(false) @EncryptResponse(false) @RequestMapping(value = "/enc" , method = RequestMethod.POST) public String enc(@RequestBody String body){ return crypto.encrypt(body); } }
定义参数解密的注解,DecryptRequest。
/** * 解密注解 * * <p>加了此注解的接口(true)将进行数据解密操作(post的body) 可 * 以放在类上,可以放在方法上 </p> * @author xiongshiyan */ @Target({ElementType.METHOD , ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DecryptRequest { /** * 是否对body进行解密 */ boolean value() default true; }
定义返回信息加密的注解,EncryptResponse。
/** * 加密注解 * * <p>加了此注解的接口(true)将进行数据加密操作 * 可以放在类上,可以放在方法上 </p> * @author 熊诗言 */ @Target({ElementType.METHOD , ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface EncryptResponse { /** * 是否对结果加密 */ boolean value() default true; }
这两个注解可以放在类和方法上,遵循一样的逻辑,即:类上的注解 && 方法上的注解,一方没有即为true,都为false为false。逻辑主要在 NeedCrypto 中。
/** * 判断是否需要加解密 * @author xiongshiyan at 2018/8/30 , contact me with email yanshixiong@126.com or phone 15208384257 */ class NeedCrypto { private NeedCrypto(){} /** * 是否需要对结果加密 * 1.类上标注或者方法上标注,并且都为true * 2.有一个标注为false就不需要加密 */ static boolean needEncrypt(MethodParameter returnType) { boolean encrypt = false; boolean classPresentAnno = returnType.getContainingClass().isAnnotationPresent(EncryptResponse.class); boolean methodPresentAnno = returnType.getMethod().isAnnotationPresent(EncryptResponse.class); if(classPresentAnno){ //类上标注的是否需要加密 encrypt = returnType.getContainingClass().getAnnotation(EncryptResponse.class).value(); //类不加密,所有都不加密 if(!encrypt){ return false; } } if(methodPresentAnno){ //方法上标注的是否需要加密 encrypt = returnType.getMethod().getAnnotation(EncryptResponse.class).value(); } return encrypt; } /** * 是否需要参数解密 * 1.类上标注或者方法上标注,并且都为true * 2.有一个标注为false就不需要解密 */ static boolean needDecrypt(MethodParameter parameter) { boolean encrypt = false; boolean classPresentAnno = parameter.getContainingClass().isAnnotationPresent(DecryptRequest.class); boolean methodPresentAnno = parameter.getMethod().isAnnotationPresent(DecryptRequest.class); if(classPresentAnno){ //类上标注的是否需要解密 encrypt = parameter.getContainingClass().getAnnotation(DecryptRequest.class).value(); //类不加密,所有都不加密 if(!encrypt){ return false; } } if(methodPresentAnno){ //方法上标注的是否需要解密 encrypt = parameter.getMethod().getAnnotation(DecryptRequest.class).value(); } return encrypt; } }
然后定义ControllerAdvice,对于请求解密的,定义 DecryptRequestBodyAdvice ,实现 RequestBodyAdvice 。
/** * 请求数据接收处理类<br> * * 对加了@Decrypt的方法的数据进行解密操作<br> * * 只对 @RequestBody 参数有效 * @author xiongshiyan */ @ControllerAdvice @ConditionalOnProperty(prefix = "spring.crypto.request.decrypt", name = "enabled" , havingValue = "true", matchIfMissing = true) public class DecryptRequestBodyAdvice implements RequestBodyAdvice { @Value("${spring.crypto.request.decrypt.charset:UTF-8}") private String charset = "UTF-8"; @Autowired @Qualifier("rrCrypto") private Crypto crypto; @Override public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return true; } @Override public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return body; } @Override public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException { if( NeedCrypto.needDecrypt(parameter) ){ return new DecryptHttpInputMessage(inputMessage , charset , crypto); } return inputMessage; } @Override public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return body; } }