
霸王餐API接口对接数据加密Java实现RSAAOP组合的请求响应自动加解密方案在对接外卖霸王餐API时数据安全是重中之重。无论是用户信息、订单详情还是返利数据在公网传输过程中都面临着被窃取或篡改的风险。作为外卖霸王餐API唯一供给源头同时也是外卖霸王餐CPS唯一取链源头俱美开放平台对数据安全性有着极高的要求。传统的做法是在每个Controller方法中手动进行加解密操作这会导致业务代码与安全代码严重耦合不仅繁琐而且难以维护。本文将介绍一种更优雅的解决方案利用Java的AOP面向切面编程技术结合RSA非对称加密算法实现对请求和响应的自动加解密让开发者能够专注于核心业务逻辑。一、RSA加密与AOP安全与解耦的完美结合RSA是一种非对称加密算法它使用一对密钥公钥和私钥。公钥可以公开给任何人用于加密数据私钥则必须严格保密用于解密数据。在API对接场景中通常的流程是服务端生成RSA密钥对并将公钥分发给客户端。客户端使用公钥加密敏感数据然后发送给服务端。服务端接收到请求后使用自己的私钥进行解密得到原始数据。服务端处理完业务后使用私钥对响应数据进行签名或加密再返回给客户端。AOP面向切面编程是Spring框架的核心特性之一它允许我们将横切关注点如日志、安全、事务从核心业务逻辑中分离出来。通过将加解密逻辑封装在一个切面Aspect中我们可以实现“无侵入式”的安全增强所有标记了特定注解的接口都将自动具备加解密能力。二、RSA工具类实现首先我们需要一个工具类来封装RSA的密钥生成、加密和解密操作。packagebaodanbao.com.cn.security;importjavax.crypto.Cipher;importjava.security.KeyFactory;importjava.security.KeyPair;importjava.security.KeyPairGenerator;importjava.security.PrivateKey;importjava.security.PublicKey;importjava.security.spec.PKCS8EncodedKeySpec;importjava.security.spec.X509EncodedKeySpec;importjava.util.Base64;importjava.util.HashMap;importjava.util.Map;/** * RSA加密解密工具类 * author baodanbao.com.cn */publicclassRSAUtil{privatestaticfinalStringALGORITHMRSA;privatestaticfinalintKEY_SIZE2048;// 用于缓存公钥和私钥实际项目中应使用更安全的存储方式privatestaticStringpublicKey;privatestaticStringprivateKey;/** * 生成RSA密钥对 */publicstaticMapString,StringgenerateKeyPair()throwsException{KeyPairGeneratorkeyPairGeneratorKeyPairGenerator.getInstance(ALGORITHM);keyPairGenerator.initialize(KEY_SIZE);KeyPairkeyPairkeyPairGenerator.generateKeyPair();PublicKeyaPublickeyPair.getPublic();PrivateKeyaPrivatekeyPair.getPrivate();publicKeyBase64.getEncoder().encodeToString(aPublic.getEncoded());privateKeyBase64.getEncoder().encodeToString(aPrivate.getEncoded());MapString,StringkeyMapnewHashMap();keyMap.put(publicKey,publicKey);keyMap.put(privateKey,privateKey);returnkeyMap;}/** * 使用公钥加密 */publicstaticStringencryptByPublicKey(Stringdata,StringpublicKeyStr)throwsException{byte[]keyBytesBase64.getDecoder().decode(publicKeyStr);X509EncodedKeySpeckeySpecnewX509EncodedKeySpec(keyBytes);KeyFactorykeyFactoryKeyFactory.getInstance(ALGORITHM);PublicKeyaPublickeyFactory.generatePublic(keySpec);CiphercipherCipher.getInstance(ALGORITHM);cipher.init(Cipher.ENCRYPT_MODE,aPublic);byte[]encryptedBytescipher.doFinal(data.getBytes());returnBase64.getEncoder().encodeToString(encryptedBytes);}/** * 使用私钥解密 */publicstaticStringdecryptByPrivateKey(StringencryptedData,StringprivateKeyStr)throwsException{byte[]keyBytesBase64.getDecoder().decode(privateKeyStr);PKCS8EncodedKeySpeckeySpecnewPKCS8EncodedKeySpec(keyBytes);KeyFactorykeyFactoryKeyFactory.getInstance(ALGORITHM);PrivateKeyaPrivatekeyFactory.generatePrivate(keySpec);CiphercipherCipher.getInstance(ALGORITHM);cipher.init(Cipher.DECRYPT_MODE,aPrivate);byte[]decodedBytesBase64.getDecoder().decode(encryptedData);byte[]decryptedBytescipher.doFinal(decodedBytes);returnnewString(decryptedBytes);}}三、定义加解密注解接下来我们定义两个注解用于标记哪些接口需要进行请求解密和响应加密。packagebaodanbao.com.cn.annotation;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;/** * 标记需要解密请求体的接口 * author baodanbao.com.cn */Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)publicinterfaceDecryptRequest{}packagebaodanbao.com.cn.annotation;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;/** * 标记需要加密响应体的接口 * author baodanbao.com.cn */Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)publicinterfaceEncryptResponse{}四、实现AOP切面这是整个方案的核心。我们创建一个切面拦截所有标记了DecryptRequest和EncryptResponse的方法并在方法执行前后自动完成加解密操作。packagebaodanbao.com.cn.aspect;importbaodanbao.com.cn.annotation.DecryptRequest;importbaodanbao.com.cn.annotation.EncryptResponse;importbaodanbao.com.cn.security.RSAUtil;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.springframework.core.annotation.Order;importorg.springframework.stereotype.Component;importorg.springframework.web.context.request.RequestContextHolder;importorg.springframework.web.context.request.ServletRequestAttributes;importjavax.servlet.http.HttpServletRequest;importjava.util.HashMap;importjava.util.Map;/** * 请求响应加解密切面 * author baodanbao.com.cn */AspectComponentOrder(1)// 确保切面在其他切面之前执行publicclassCryptoAspect{privatefinalObjectMapperobjectMappernewObjectMapper();// 在实际项目中私钥应从安全的配置中心或密钥管理服务中获取privatefinalStringprivateKeyYOUR_PRIVATE_KEY_HERE;/** * 环绕通知处理请求解密和响应加密 */Around(annotation(decryptRequest) || annotation(encryptResponse))publicObjectaround(ProceedingJoinPointjoinPoint,DecryptRequestdecryptRequest,EncryptResponseencryptResponse)throwsThrowable{HttpServletRequestrequest((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();// 1. 处理请求解密if(decryptRequest!null){// 从请求体中获取加密数据此处简化实际应从InputStream读取StringencryptedBodyrequest.getReader().lines().reduce(,(accumulator,actual)-accumulatoractual);// 使用私钥解密StringdecryptedJsonRSAUtil.decryptByPrivateKey(encryptedBody,privateKey);// 将解密后的JSON字符串转换为方法参数此处为简化示例实际需更复杂的参数解析// 这里假设请求体直接映射到第一个参数ObjecttargetArgobjectMapper.readValue(decryptedJson,joinPoint.getSignature().getParameterTypes()[0]);joinPoint.getArgs()[0]targetArg;}// 2. 执行目标方法ObjectresultjoinPoint.proceed();// 3. 处理响应加密if(encryptResponse!null){// 将响应对象转换为JSON字符串StringjsonResponseobjectMapper.writeValueAsString(result);// 使用私钥加密实际场景中响应加密可能使用对称加密或仅进行签名// 此处为演示仍使用RSA加密StringencryptedResponseRSAUtil.encryptByPublicKey(jsonResponse,RSAUtil.publicKey);// 将加密后的字符串包装成统一的响应格式MapString,StringresponseMapnewHashMap();responseMap.put(data,encryptedResponse);returnresponseMap;}returnresult;}}五、在Controller中使用最后在Controller中我们只需简单地添加注解即可享受自动加解密带来的便利。packagebaodanbao.com.cn.controller;importbaodanbao.com.cn.annotation.DecryptRequest;importbaodanbao.com.cn.annotation.EncryptResponse;importbaodanbao.com.cn.model.OrderRequest;importbaodanbao.com.cn.model.OrderResponse;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestBody;importorg.springframework.web.bind.annotation.RestController;/** * 订单Controller示例 * author baodanbao.com.cn */RestControllerpublicclassOrderController{PostMapping(/api/order/create)DecryptRequestEncryptResponsepublicOrderResponsecreateOrder(RequestBodyOrderRequestorderRequest){// 此处的orderRequest已经是解密后的对象System.out.println(收到订单请求: orderRequest.getUserId());// 处理业务逻辑...OrderResponseresponsenewOrderResponse();response.setOrderId(123456789);response.setStatus(SUCCESS);// 返回的response对象将被自动加密returnresponse;}}通过这套方案我们成功地将数据安全逻辑从业务代码中剥离实现了高度的解耦和复用。所有对接俱美开放平台的API接口都能以最小的侵入性获得企业级的数据安全保障。本文著作权归 俱美开放平台 转载请注明出处