admin 管理员组文章数量: 1087652
SpringBoot中接口加密解密统一处理
项目参见
我们与客户端的接口交互中,为了更高的安全性,我们可能需要对接口加密(请求参数加密,服务端解密)、返回信息加密(服务端加密,客户端解密),但是也不是所有的接口都这样,有些接口可能不需要,我们可以使用注解来轻松达到此要求。
将接口参数的加密解密和返回信息的加密解密分开,分别定义注解,利用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 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;@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) {return true;}@Overridepublic Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return body;}@Overridepublic 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;}@Overridepublic Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) {return body;}
}
标上注解 ConditionalOnProperty 表示只有条件为true的时候才开启解密功能,一个配置即可打开或者关闭解密功能。真正的解密逻辑留给 DecryptHttpInputMessage , 它又委托给 Crypto。
/**** @author xiongshiyan*/
public class DecryptHttpInputMessage implements HttpInputMessage {private HttpInputMessage inputMessage;private String charset;private Crypto crypto;public DecryptHttpInputMessage(HttpInputMessage inputMessage, String charset , Crypto crypto) {this.inputMessage = inputMessage;this.charset = charset;this.crypto = crypto;}@Overridepublic InputStream getBody() throws IOException {String content = IoUtil.read(inputMessage.getBody() , charset);String decryptBody = crypto.decrypt(content, charset);return new ByteArrayInputStream(decryptBody.getBytes(charset));}@Overridepublic HttpHeaders getHeaders() {return inputMessage.getHeaders();}
}
对于返回值加密,定义 EncryptResponseBodyAdvice,实现 ResponseBodyAdvice。
/*** 请求响应处理类<br>* * 对加了@Encrypt的方法的数据进行加密操作* * @author 熊诗言**/
@ControllerAdvice
@ConditionalOnProperty(prefix = "spring.crypto.response.encrypt", name = "enabled" , havingValue = "true", matchIfMissing = true)
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {@Value("${spring.crypto.request.decrypt.charset:UTF-8}")private String charset = "UTF-8";@Autowired@Qualifier("rrCrypto")private Crypto crypto;@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {boolean encrypt = NeedCrypto.needEncrypt(returnType);if( !encrypt ){return body;}if(!(body instanceof ResponseMsg)){return body;}//只针对ResponseMsg的data进行加密ResponseMsg responseMsg = (ResponseMsg) body;Object data = responseMsg.getData();if(null == data){return body;}String xx;Class<?> dataClass = data.getClass();if(dataClass.isPrimitive() || (data instanceof String)){xx = String.valueOf(data);}else {//JavaBean、Map、List等先序列化if(List.class.isAssignableFrom(dataClass)){xx = JsonUtil.serializeList((List<Object>) data);}else if(Map.class.isAssignableFrom(dataClass)){xx = JsonUtil.serializeMap((Map<String, Object>) data);}else {xx = JsonUtil.serializeJavaBean(data);}}responseMsg.setData(crypto.encrypt(xx, charset));return responseMsg;}}
真正的加密逻辑委托给 Crypto ,这是一个加密解密的接口,有很多实现类,参见 :
/*** Request-Response加解密体系的加解密方式* @author xiongshiyan at 2018/8/14 , contact me with email yanshixiong@126 or phone 15208384257*/
@Configuration
public class RRCryptoConfig {/*** 加密解密方式使用一样的*/@Bean("rrCrypto")public Crypto rrCrypto(){return new AesCrypto("密钥key");}
}
至此,一个完美的对接口的加密解密就实现了。
本文标签: SpringBoot中接口加密解密统一处理
版权声明:本文标题:SpringBoot中接口加密解密统一处理 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/p/1700300415a386614.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论