背景
在我当前的项目中,有许多接口都需要加解密,保证数据的安全性,为了抽象出这一类接口的共性。
而在我们的接口设计中,加密后的参数是以整个body来传送密文的,并非json中的某一个字段,所以本文的方案,也可以用于自定义传输数据类型。
本文通过RequestBodyAdvice、ResponseBodyAdvice来实现这一需求。
定义注解,标识请求是否需要加解密
1 2 3 4 5 6 7
| @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface SecurityInterface { boolean encryptRequest() default true;
boolean encryptResponse() default true; }
|
请求参数解密
拦截请求,对参数解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import org.springframework.core.MethodParameter; import org.springframework.http.HttpInputMessage; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Type;
@ControllerAdvice public class DecryptRequestBodyAdvice implements RequestBodyAdvice { @Override public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { Method method = methodParameter.getMethod(); SecurityInterface annotation = method.getAnnotation(SecurityInterface.class); if (annotation == null) { annotation = method.getClass().getAnnotation(SecurityInterface.class); } if (annotation == null) { return false; } return annotation.encryptRequest(); }
@Override public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException { HttpInputMessage returnInputMessage = new DecryptHttpInputMessage(inputMessage.getHeaders(), inputMessage.getBody()); return returnInputMessage; }
@Override public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return body; }
@Override public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return body; } }
|
解密的具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import com.guys.txm.core.util.EncryptUtil; import com.guys.txm.core.util.StreamUtil; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage;
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream;
public class DecryptHttpInputMessage implements HttpInputMessage { private HttpHeaders headers = null; private InputStream is = null; private static final String ENC_KEY = "xxxx"; private static final String ENC_E = "xxxx";
public DecryptHttpInputMessage(HttpHeaders headers, InputStream is) { this.headers = headers; this.is = is; }
@Override public InputStream getBody() throws IOException { ByteArrayOutputStream out = null; try { out = StreamUtil.copy(is); byte[] temp = EncryptUtil.decodeBase64(out.toByteArray()); byte[] body = EncryptUtil.aesCBCDecrypt(temp, ENC_KEY, ENC_E); return new ByteArrayInputStream(body); } catch (Exception e) { throw new IOException(e.getMessage(), e); } finally { StreamUtil.close(is); StreamUtil.close(out); } }
@Override public HttpHeaders getHeaders() { return headers; } }
|
返回参数加密
同样的方式,使用ResponseBodyAdvice拦截返回值,在此可以进行数据加密。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import org.springframework.web.bind.annotation.ControllerAdvice;
@ControllerAdvice public class EncryptResponseBodyAdvice implements ResponseBodyAdvice { @Override public boolean supports(MethodParameter returnType, Class converterType) { return false; }
@Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { return null; } }
|