一、为什么需要接口数据加解密?
在金融支付、用户隐私信息传输等场景中,接口数据若以明文传输,极易被中间人攻击窃取。例如:
- 用户登录时的密码、身份证号等敏感信息
- 企业间数据交互的核心业务参数
- 移动端与后台交互的 token 凭证
spring boot 提供了多种优雅的加解密实现方案,既能保证数据安全,又能最小化业务侵入性。本文将从原理到实战,带你掌握三种主流实现方式。
二、核心加解密算法选择
1. 对称加密(aes)
优势:加密速度快,适合大流量数据传输缺点:密钥需安全存储,适合客户端与服务端一对一场景
// aes 工具类(128位密钥)
public class aesutils {
private static final string key = "your_16bit_secret_key";
private static final string algorithm = "aes/ecb/pkcs5padding";
public static string encrypt(string data) throws exception {
cipher cipher = cipher.getinstance(algorithm);
cipher.init(cipher.encrypt_mode, new secretkeyspec(key.getbytes(), "aes"));
return base64.getencoder().encodetostring(cipher.dofinal(data.getbytes()));
}
public static string decrypt(string data) throws exception {
cipher cipher = cipher.getinstance(algorithm);
cipher.init(cipher.decrypt_mode, new secretkeyspec(key.getbytes(), "aes"));
return new string(cipher.dofinal(base64.getdecoder().decode(data)));
}
}
2. 非对称加密(rsa)
优势:密钥对机制,适合证书认证场景缺点:加密效率低,通常用于加密对称密钥
// rsa 工具类(生成公钥私钥对)
public class rsautils {
private static final int key_size = 1024;
private static final string algorithm = "rsa";
public static map<string, string> generatekeypair() throws exception {
keypairgenerator generator = keypairgenerator.getinstance(algorithm);
generator.initialize(key_size);
keypair pair = generator.generatekeypair();
return map.of(
"publickey", base64.getencoder().encodetostring(pair.getpublic().getencoded()),
"privatekey", base64.getencoder().encodetostring(pair.getprivate().getencoded())
);
}
}
三、实战方案一:基于 aop 的透明加解密
1. 核心原理
通过自定义注解标记需要加解密的接口,利用 spring aop 在方法调用前后自动处理加解密逻辑,实现业务代码零侵入。
2. 实现步骤
(1)定义加解密注解
@target(elementtype.method)
@retention(retentionpolicy.runtime)
public @interface encrypt {
// 排除字段(如时间戳等无需加密字段)
string[] excludefields() default {};
}
@target(elementtype.method)
@retention(retentionpolicy.runtime)
public @interface decrypt {
// 解密失败是否抛出异常
boolean throwonfailure() default true;
}
(2)编写 aop 切面
@aspect
@component
public class dataencryptaspect {
@around("@annotation(encrypt)")
public object encryptaround(proceedingjoinpoint joinpoint) throws throwable {
// 执行原始方法
object result = joinpoint.proceed();
// 对响应结果进行aes加密
return aesutils.encrypt(json.tojsonstring(result));
}
@around("@annotation(decrypt)")
public object decryptaround(proceedingjoinpoint joinpoint) throws throwable {
// 获取请求参数(假设参数为json字符串)
object[] args = joinpoint.getargs();
string encrypteddata = (string) args[0];
// 解密请求参数
string decrypteddata = aesutils.decrypt(encrypteddata);
// 替换原始参数为解密后的数据
args[0] = decrypteddata;
return joinpoint.proceed(args);
}
}
(3)控制器使用示例
@restcontroller
@requestmapping("/api")
public class usercontroller {
@postmapping("/register")
@decrypt
public userregisterresponse register(@requestbody string encrypteddata) {
// 处理解密后的明文数据
userregisterrequest request = json.parseobject(encrypteddata, userregisterrequest.class);
// 业务逻辑...
return new userregisterresponse("注册成功", request.getuserid());
}
@getmapping("/profile")
@encrypt
public userprofile getprofile(@requestparam string userid) {
// 业务逻辑获取用户信息
return new userprofile("张三", "138****1234");
}
}
3. 方案优势
- 低侵入性:仅需在接口方法添加注解
- 灵活配置:可自定义排除字段和异常处理策略
- 适用场景:适合对单个接口细粒度控制的场景
四、实战方案二:全局过滤器实现请求响应加解密
1. 核心原理
通过实现 filter 或 handlerinterceptor,在请求进入控制器前解密参数,响应离开前加密结果,实现全局统一加解密。
2. 实现步骤
(1)自定义加解密过滤器
@component
public class dataencryptfilter implements filter {
@override
public void dofilter(servletrequest request, servletresponse response, filterchain chain)
throws ioexception, servletexception {
// 处理请求解密(假设请求体为加密的json)
httpservletrequest httprequest = (httpservletrequest) request;
string encryptedbody = ioutils.tostring(httprequest.getinputstream(), standardcharsets.utf_8);
string decryptedbody = aesutils.decrypt(encryptedbody);
// 包装请求体为可重复读取的流
httpservletrequestwrapper requestwrapper = new httpservletrequestwrapper(httprequest, decryptedbody);
// 处理响应加密
final bytearrayoutputstream buffer = new bytearrayoutputstream();
httpservletresponsewrapper responsewrapper = new httpservletresponsewrapper((httpservletresponse) response, buffer);
chain.dofilter(requestwrapper, responsewrapper);
// 对响应结果加密并写出
string encryptedresult = aesutils.encrypt(buffer.tostring());
response.getwriter().write(encryptedresult);
}
}
// 请求包装类(重写getinputstream)
class httpservletrequestwrapper extends httpservletrequestwrapper {
private final string body;
public httpservletrequestwrapper(httpservletrequest request, string body) {
super(request);
this.body = body;
}
@override
public servletinputstream getinputstream() throws ioexception {
final bytearrayinputstream bis = new bytearrayinputstream(body.getbytes());
return new servletinputstream() {
@override
public int read() throws ioexception {
return bis.read();
}
// 省略其他抽象方法实现
};
}
}
(2)配置过滤器生效
@configuration
public class filterconfig {
@bean
public filterregistrationbean<dataencryptfilter> encryptfilterregistration() {
filterregistrationbean<dataencryptfilter> registration = new filterregistrationbean<>();
registration.setfilter(new dataencryptfilter());
registration.addurlpatterns("/api/v1/**"); // 配置需要加解密的接口路径
registration.setorder(ordered.highest_precedence); // 保证过滤器优先执行
return registration;
}
}
3. 方案优势
- 全局统一:一次配置,所有接口自动加解密
- 高性能:基于流处理,避免反射带来的性能损耗
- 适用场景:适合前后端分离项目的全局数据加密
五、实战方案三:自定义 messageconverter 实现透明加解密
1. 核心原理
重写 spring mvc 的 httpmessageconverter,在请求参数解析和响应数据序列化阶段自动完成加解密,与框架深度整合。
2. 实现步骤
(1)自定义加解密转换器
public class encryptinghttpmessageconverter extends abstracthttpmessageconverter<object> {
@override
protected boolean supports(class<?> clazz) {
return true; // 支持所有类型
}
@override
protected object readinternal(class<?> clazz, httpinputmessage inputmessage)
throws ioexception, httpmessagenotreadableexception {
// 读取加密的请求体并解密
string encrypted = ioutils.tostring(inputmessage.getbody(), standardcharsets.utf_8);
string decrypted = aesutils.decrypt(encrypted);
return json.parseobject(decrypted, clazz);
}
@override
protected void writeinternal(object object, httpoutputmessage outputmessage)
throws ioexception, httpmessagenotwritableexception {
// 将响应对象加密后写出
string plain = json.tojsonstring(object);
string encrypted = aesutils.encrypt(plain);
outputmessage.getbody().write(encrypted.getbytes(standardcharsets.utf_8));
}
}
(2)注册自定义转换器
@configuration
public class webmvcconfig implements webmvcconfigurer {
@override
public void configuremessageconverters(list<httpmessageconverter<?>> converters) {
converters.add(new encryptinghttpmessageconverter());
// 保留默认转换器(可选)
// converters.addall(collections.singletonlist(new mappingjackson2httpmessageconverter()));
}
}
3. 方案优势
- 框架级整合:与 spring mvc 数据绑定机制深度融合
- 类型安全:自动处理对象与加密字符串的转换
- 适用场景:适合对请求 / 响应格式有严格控制的场景
六、三种方案对比与选型建议
方案一:aop 注解
侵入性:低
性能:中灵
活性:接口级控制
适用场景:部分接口需要加解密
方案二:全局过滤器
侵入性:中性能:高
灵活性:路径级控制
适用场景:前后端分离项目全局加密
方案三:messageconverter
侵入性:高
性能:最高
灵活性:框架级控制
适用场景:统一请求响应格式场景
七、生产环境最佳实践
1. 密钥管理方案
- 禁止硬编码:通过 spring config 或配置中心(如 nacos)管理密钥
- 密钥轮换:定期生成新密钥,旧密钥逐步淘汰
- 硬件安全:敏感系统使用 hsm(硬件安全模块)存储密钥
2. 异常处理机制
@restcontrolleradvice
public class encryptexceptionhandler {
@exceptionhandler(decryptionexception.class)
public responseentity<string> handledecryptionerror(decryptionexception e) {
return responseentity.status(httpstatus.bad_request)
.body("数据解密失败:" + e.getmessage());
}
}
3. 性能优化技巧
- 压缩后加密:对大体积数据先压缩再加密(gzip 压缩可减少 50% 数据量)
- 异步加解密:使用 completablefuture 实现加解密与业务逻辑并行处理
- 缓存加密结果:对高频访问接口的加密结果进行缓存
八、总结
spring boot 提供了从接口级到框架级的完整加解密解决方案,核心是根据业务场景选择合适的实现方式:
- 追求灵活性选 aop 注解
- 追求统一性选 全局过滤器
- 追求框架整合选 messageconverter
无论哪种方案,都需注意密钥安全和异常处理。通过本文的源码示例,开发者可快速在项目中落地接口数据加解密功能,在保障数据安全的同时,最小化对现有业务的影响。
以上就是springboot实现接口数据加解密的三种实战方案的详细内容,更多关于springboot接口数据加解密的资料请关注代码网其它相关文章!
发表评论