在当今数据泄露频发的网络环境中,接口安全已成为开发者不可忽视的核心议题。rsa+aes混合加密方案因其安全性高、性能优越而被广泛采用:
- rsa(非对称加密):解决密钥分发难题,适合加密小数据(如aes密钥)。
- aes(对称加密):高效加密大量数据,适合传输业务参数。
本文将深入springboot框架,手把手演示如何通过自定义注解+拦截器实现接口的自动化加解密,并提供完整工具类、aop切片代码及安全优化策略。
一、项目依赖与环境准备
1.1 maven依赖配置
在pom.xml
中添加必要的依赖:
<dependencies> <!-- springboot web支持 --> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <!-- json处理工具 --> <dependency> <groupid>com.alibaba</groupid> <artifactid>fastjson</artifactid> <version>1.2.83</version> </dependency> <!-- 加密算法扩展(支持bcrypt等) --> <dependency> <groupid>org.bouncycastle</groupid> <artifactid>bcprov-jdk15on</artifactid> <version>1.70</version> </dependency> <!-- lombok简化代码 --> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> <optional>true</optional> </dependency> </dependencies>
1.2 密钥生成与配置
rsa密钥对生成:
// 工具类:生成rsa密钥对 public class keygenerator { public static void main(string[] args) throws exception { keypairgenerator keygen = keypairgenerator.getinstance("rsa"); keygen.initialize(2048); // 2048位密钥长度 keypair keypair = keygen.generatekeypair(); // 公钥(客户端使用) string publickeystr = base64.getencoder().encodetostring(keypair.getpublic().getencoded()); system.out.println("public key:\n" + publickeystr); // 私钥(服务端使用) string privatekeystr = base64.getencoder().encodetostring(keypair.getprivate().getencoded()); system.out.println("private key:\n" + privatekeystr); } }
配置文件中存储密钥:
# application.yml encrypt: rsa: public-key: "miibijanbgkqhkig9w0baqefaaocaq8amiibcgkcaqea..." private-key: "miievqibadanbgkqhkig9w0baqefaascbkcwggsjageaaoibaqc..."
二、加密工具类实现
2.1 rsa工具类
import org.bouncycastle.jce.provider.bouncycastleprovider; import javax.crypto.cipher; import java.security.*; import java.security.spec.pkcs8encodedkeyspec; import java.security.spec.x509encodedkeyspec; import java.util.base64; public class rsautil { static { security.addprovider(new bouncycastleprovider()); // 添加bc支持 } // 使用公钥加密 public static string encrypt(string data, string publickeystr) throws exception { publickey publickey = getpublickeyfrombase64(publickeystr); cipher cipher = cipher.getinstance("rsa/ecb/pkcs1padding"); cipher.init(cipher.encrypt_mode, publickey); return base64.getencoder().encodetostring(cipher.dofinal(data.getbytes())); } // 使用私钥解密 public static string decrypt(string encrypteddata, string privatekeystr) throws exception { privatekey privatekey = getprivatekeyfrombase64(privatekeystr); cipher cipher = cipher.getinstance("rsa/ecb/pkcs1padding"); cipher.init(cipher.decrypt_mode, privatekey); return new string(cipher.dofinal(base64.getdecoder().decode(encrypteddata))); } // 从base64字符串生成公钥 private static publickey getpublickeyfrombase64(string keystr) throws exception { byte[] keybytes = base64.getdecoder().decode(keystr); x509encodedkeyspec spec = new x509encodedkeyspec(keybytes); keyfactory keyfactory = keyfactory.getinstance("rsa"); return keyfactory.generatepublic(spec); } // 从base64字符串生成私钥 private static privatekey getprivatekeyfrombase64(string keystr) throws exception { byte[] keybytes = base64.getdecoder().decode(keystr); pkcs8encodedkeyspec spec = new pkcs8encodedkeyspec(keybytes); keyfactory keyfactory = keyfactory.getinstance("rsa"); return keyfactory.generateprivate(spec); } }
2.2 aes工具类
import javax.crypto.cipher; import javax.crypto.spec.ivparameterspec; import javax.crypto.spec.secretkeyspec; import java.nio.charset.standardcharsets; import java.security.securerandom; import java.util.base64; public class aesutil { // aes加密模式:cbc + pkcs7padding private static final string aes_mode = "aes/cbc/pkcs7padding"; private static final int iv_length = 16; // 初始化向量长度 // 生成随机aes密钥 public static string generateaeskey() throws exception { byte[] key = new byte[16]; // 128位密钥 new securerandom().nextbytes(key); return base64.getencoder().encodetostring(key); } // aes加密 public static string encrypt(string data, string aeskey) throws exception { byte[] keybytes = base64.getdecoder().decode(aeskey); secretkeyspec keyspec = new secretkeyspec(keybytes, "aes"); byte[] iv = new byte[iv_length]; new securerandom().nextbytes(iv); ivparameterspec ivspec = new ivparameterspec(iv); cipher cipher = cipher.getinstance(aes_mode); cipher.init(cipher.encrypt_mode, keyspec, ivspec); byte[] encryptedbytes = cipher.dofinal(data.getbytes(standardcharsets.utf_8)); // 将iv和密文合并返回 byte[] result = new byte[iv.length + encryptedbytes.length]; system.arraycopy(iv, 0, result, 0, iv.length); system.arraycopy(encryptedbytes, 0, result, iv.length, encryptedbytes.length); return base64.getencoder().encodetostring(result); } // aes解密 public static string decrypt(string encrypteddata, string aeskey) throws exception { byte[] data = base64.getdecoder().decode(encrypteddata); byte[] iv = new byte[iv_length]; byte[] encryptedbytes = new byte[data.length - iv_length]; system.arraycopy(data, 0, iv, 0, iv_length); system.arraycopy(data, iv_length, encryptedbytes, 0, encryptedbytes.length); secretkeyspec keyspec = new secretkeyspec(base64.getdecoder().decode(aeskey), "aes"); ivparameterspec ivspec = new ivparameterspec(iv); cipher cipher = cipher.getinstance(aes_mode); cipher.init(cipher.decrypt_mode, keyspec, ivspec); return new string(cipher.dofinal(encryptedbytes), standardcharsets.utf_8); } }
三、自定义注解与aop切面
3.1 自定义解密注解 @requestrsa
import java.lang.annotation.*; @target({elementtype.type, elementtype.method}) @retention(retentionpolicy.runtime) @documented public @interface requestrsa { // 标记需要解密的接口 }
3.2 aop切面实现自动解密
import com.alibaba.fastjson.jsonobject; import org.aspectj.lang.proceedingjoinpoint; import org.aspectj.lang.annotation.around; import org.aspectj.lang.annotation.aspect; import org.aspectj.lang.reflect.methodsignature; import org.springframework.beans.factory.annotation.value; import org.springframework.core.annotation.order; import org.springframework.stereotype.component; import org.springframework.web.bind.annotation.requestbody; import javax.servlet.http.httpservletrequest; import java.io.bufferedreader; import java.io.inputstreamreader; import java.lang.reflect.method; import java.lang.reflect.parameter; /** * rsa+aes接口解密切面 */ @aspect @component @order(1) public class requestrsaaspect { @value("${encrypt.rsa.private-key}") private string privatekey; /** * 拦截带有@requestrsa注解的方法 */ @around("@annotation(requestrsa) || @within(requestrsa)") public object decryptrequest(proceedingjoinpoint joinpoint, requestrsa requestrsa) throws throwable { // 获取请求体 httpservletrequest request = ((servletrequestattributes) requestcontextholder.getrequestattributes()).getrequest(); string body = getrequestbody(request); // 解析加密参数 jsonobject encryptedjson = jsonobject.parseobject(body); string sym = encryptedjson.getstring("sym"); // rsa加密的aes密钥 string asy = encryptedjson.getstring("asy"); // aes加密的业务数据 // rsa解密获取aes密钥 string aeskey = rsautil.decrypt(sym, privatekey); // aes解密业务数据 string decrypteddata = aesutil.decrypt(asy, aeskey); // 将解密后的数据注入方法参数 object[] args = injectdecrypteddata(joinpoint, decrypteddata); return joinpoint.proceed(args); } /** * 读取请求体内容 */ private string getrequestbody(httpservletrequest request) throws exception { stringbuilder sb = new stringbuilder(); bufferedreader reader = new bufferedreader(new inputstreamreader(request.getinputstream())); string line; while ((line = reader.readline()) != null) { sb.append(line); } return sb.tostring(); } /** * 将解密后的数据注入方法参数 */ private object[] injectdecrypteddata(proceedingjoinpoint joinpoint, string decrypteddata) throws exception { methodsignature signature = (methodsignature) joinpoint.getsignature(); method method = signature.getmethod(); parameter[] parameters = method.getparameters(); object[] args = joinpoint.getargs(); for (int i = 0; i < parameters.length; i++) { if (parameters[i].isannotationpresent(requestbody.class)) { // 将解密后的json字符串反序列化为目标对象 args[i] = jsonobject.parseobject(decrypteddata, parameters[i].getparameterizedtype()); } } return args; } }
四、客户端加密逻辑实现
4.1 客户端加密流程
public class clientencryption { public static void main(string[] args) throws exception { // 1. 随机生成aes密钥和iv string aeskey = aesutil.generateaeskey(); string aesiv = aesutil.generateaeskey(); // 实际应使用securerandom生成iv // 2. 构造请求参数 jsonobject requestdata = new jsonobject(); requestdata.put("username", "user123"); requestdata.put("password", "securepass"); // 3. aes加密业务数据 string asy = aesutil.encrypt(requestdata.tojsonstring(), aeskey); // 4. 构造aes密钥信息 jsonobject aesinfo = new jsonobject(); aesinfo.put("key", aeskey); aesinfo.put("keyvi", aesiv); aesinfo.put("time", system.currenttimemillis()); // 5. rsa加密aes密钥信息 string sym = rsautil.encrypt(aesinfo.tojsonstring(), "服务器公钥"); // 6. 构造最终请求体 jsonobject finalrequest = new jsonobject(); finalrequest.put("sym", sym); finalrequest.put("asy", asy); system.out.println("加密后请求体:\n" + finalrequest.tojsonstring()); } }
五、服务器端接口示例
5.1 带解密注解的接口
@restcontroller @requestmapping("/api") public class securecontroller { @postmapping("/login") @requestrsa // 标记需要解密 public response login(@requestbody loginrequest request) { // 直接使用解密后的参数 return new response("登录成功", request.getusername()); } } // 请求参数类 @data public class loginrequest { private string username; private string password; }
六、安全优化与最佳实践
6.1 密钥管理策略
- 私钥存储:使用配置中心(如nacos)或密钥管理服务(如aws kms)。
- 公钥分发:通过https协议分发,避免中间人攻击。
- 定期轮换:每30天更新一次rsa密钥对。
6.2 时间戳防重放攻击
在aop切面中添加时间戳校验:
private void validatetimestamp(jsonobject aesinfo) { long requesttime = aesinfo.getlong("time"); long currenttime = system.currenttimemillis(); if (math.abs(currenttime - requesttime) > 60000) { // 允许1分钟内 throw new runtimeexception("请求超时,请重试"); } }
6.3 性能优化建议
- 缓存公钥/私钥对象:避免重复解析base64字符串。
- 异步解密:对高并发接口采用线程池异步处理。
- 压缩数据:对加密后的数据进行gzip压缩减少传输体积。
七、完整调用流程图解
客户端流程: [生成aes密钥] → [aes加密数据] → [构造aes信息] → [rsa加密aes信息] → [发送请求] 服务端流程: [接收请求] → [aop切面拦截] → [rsa解密aes密钥] → [aes解密业务数据] → [注入参数执行接口]
八、 构建高安全接口的核心要点
模块 | 关键点 | 注意事项 |
---|---|---|
密钥管理 | rsa公私钥分离存储,aes密钥动态生成 | 私钥需加密存储,定期轮换 |
加密流程 | rsa加密aes密钥,aes加密业务数据 | 确保iv随机性,避免重复使用 |
aop切面设计 | 自动拦截、解密、参数注入 | 处理异常情况(如密钥错误、时间戳过期) |
安全防护 | 时间戳防重放、https传输、数据完整性校验 | 避免硬编码密钥,使用配置中心 |
九、 工具类与依赖说明
- bouncy castle:提供扩展的加密算法支持(如pkcs7padding)。
- fastjson:高效json序列化/反序列化。
- lombok:简化pojo类的编写(如
@data
注解)。
以上就是springboot实现rsa+aes自动接口解密的实战指南的详细内容,更多关于springboot rsa aes接口解密的资料请关注代码网其它相关文章!
发表评论