方案开发背景
随着企业信息化的发展和业务需求的增加,与外部系统交互的需求也越来越多。服务端的系统在公网暴露接口时为了安全考虑会增加很多安全性处理。今天分享一个我们使用的接口优雅、透明加解密方案,分客户端调用与服务端接收两部分
思路
其实代码很简单放到后面来写,重要的是对于加解密的思考
首先为什么要加解密?
我能想到的场景就是客户端涉及增删改的请求报文以及服务端的响应报文防止明文泄露,实际上我们也是为此而做的
哪些操作要加解密?
HTTP协议常用的无非就是get请求获取数据、post、put、delete(后续只用post做示例)做增删改操作。协议中携带数据的方式也就是2个地方:url以及请求体
所以最后我们分析得出结论:get请求无需加密;post请求需要加密;所有的响应报文都需要进行加密
客户端声明式调用以及透明加解密处理
客户端我们的方案是基于Forest框架+自定义转换器实现,先列出一些基础信息
java: 1.8
springboot: 2.3.4
forest: 1.5.19,一个声明式HTTP远程调用框架
其实代码很简单
1. springboot集成Forest
这个就不写了,官方内容又全又明白
2. 自定义转换器
开发前可自行先去官网了解一下转换器的基础
Forest作为远程调用客户端,存在发送报文、接收报文两个动作:
- 将java对象 -----> http报文内容称为
encoder也叫序列化,默认框架自动识别,使用@BodyType可以自定义转换 - 将http返回报文 -----> 接口返回值称为
decoder也叫反序列化,默认框架自动识别,自定义转换可使用httpMethod相关注解中的dataType属性或decoder指定转换器
这里发现个问题,在forest 1.5.19版本中,httpMethod相关注解中存在
encoder属性,但通过此属性设置的编码器不生效
那么事情就很简单了,我们只要自定义一个转换器然后实现序列化和反序列化操作即可
翻看源码能发现两个顶级接口:
ForestConverter
这里差点被命名迷惑:虽然命名是converter并且注释写着:序列化以及反序列化,但其实他只包含反序列化相关操作
ForestEncoder
此接口只包含序列化相关操作
下面是源码分析以及实现思路:
翻看官方的转换器源码,发现
ForestJsonConverter实现了上面两个顶级接口,表示这是一个支持序列化和反序列化的json格式转换器;然后发现他实现了encodeRequestBody方法:这个方法主要是遍历转换不同类型的请求体为json格式。接口默认有个实现类ForestFastjsonConverter所以我们最终只需要继承
ForestFastjsonConverter类,并重写其中序列化和反序列化过程中涉及到的方法操作即可。
最后是代码呈现
@Slf4j
public class AesForestConverter extends ForestFastjsonConverter {
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
@Override
public <T> T convertToJavaObject(String source, Type targetType) {
try {
byte[] decrypt = AESUtils.decrypt(Base64Utils.decode(source), Decrypt.AES_KEY.getText());
return JSON.parseObject(new String(decrypt, DEFAULT_CHARSET), targetType);
} catch (Exception e) {
throw new SystemException("AES解密异常", e);
}
}
@Override
public <T> T convertToJavaObject(byte[] source, Class<T> targetType, Charset charset) {
if (charset == null) {
charset = DEFAULT_CHARSET;
}
try {
final String sourceStr = new String(source, charset);
byte[] decrypt = AESUtils.decrypt(Base64Utils.decode(sourceStr), Decrypt.AES_KEY.getText());
final String str = new String(decrypt, charset);
return JSON.parseObject(str, targetType);
} catch (Exception e) {
throw new SystemException("AES解密异常", e);
}
}
@Override
public <T> T convertToJavaObject(byte[] source, Type targetType, Charset charset) {
if (charset == null) {
charset = DEFAULT_CHARSET;
}
try {
final String sourceStr = new String(source, charset);
byte[] decrypt = AESUtils.decrypt(Base64Utils.decode(sourceStr), Decrypt.AES_KEY.getText());
final String str = new String(decrypt, charset);
return JSON.parseObject(str, targetType);
} catch (Exception e) {
throw new SystemException("AES解密异常", e);
}
}
@Override
public ForestDataType getDataType() {
return ForestDataType.createDataType("aes", true);
}
@Override
public String encodeToString(Object obj) {
try {
return URLEncoder.encode(Base64Utils.encode(AESUtils.encrypt(JSON.toJSONString(obj).getBytes(StandardCharsets.UTF_8), Decrypt.AES_KEY.getText())), "UTF-8");
} catch (Exception e) {
throw new SystemException("加密异常", e);
}
}
}
springboot注入及声明式使用
注册转换器Bean
@Configuration
public class ForestConfig {
@Bean
public AesForestConverter aesForestConverter() {
return new AesForestConverter();
}
}
大功告成,来看几个调用示例
@BaseRequest(baseURL = "http://localhost:8080/ttest")
public interface TestApi {
@Get(url = "/get", decoder = AesForestConverter.class)
Resp<TestRespDTO> get(@Query("id") Integer id, @Query("name") String name);
@Post(url = "/wwwPost", decoder = AesForestConverter.class)
@BodyType(type = "aes", encoder = AesForestConverter.class)
Resp<TestRespDTO> wwwPost(@Body("id") Integer id, @Body("name") String name);
@Post(url = "/jsonPost", decoder = AesForestConverter.class)
@BodyType(type = "aes", encoder = AesForestConverter.class)
Resp<TestRespDTO> jsonPost(@JSONBody("id") Integer id, @JSONBody("name") String name);
}
//调用代码
final Resp<TestRespDTO> getResp = testApi.get(id, name);
final Resp<TestRespDTO> wwwResp = testApi.wwwPost(id, name);
final Resp<TestRespDTO> jsonResp = testApi.jsonPost(id, name);
下面是生成的请求报文
get方法
GET http://localhost:8088/coder-tools-env/ttest/get?id=1&name=wm
wwwPost方法
POST http://localhost:8088/coder-tools-env/ttest/wwwPost HTTP
Body: FQpEaNu8jbRpIWe4%2F%2FBY0FZ%2ByjIEBgJTL20P1v5DIE8%3D
jsonPost方法
POST http://localhost:8088/coder-tools-env/ttest/jsonPost HTTP
Headers:
Content-Type: application/json
Body: FQpEaNu8jbRpIWe4%2F%2FBY0FZ%2ByjIEBgJTL20P1v5DIE8%3D
以上是客户端内容,服务端下篇分享