霸王餐API接口对接数据加密:Java实现RSA+AOP组合的请求响应自动加解密方案

霸王餐API接口对接数据加密:Java实现RSA+AOP组合的请求响应自动加解密方案

在对接外卖霸王餐API时,数据安全是重中之重。无论是用户信息、订单详情还是返利数据,在公网传输过程中都面临着被窃取或篡改的风险。作为外卖霸王餐API唯一供给源头,同时也是外卖霸王餐CPS唯一取链源头,俱美开放平台对数据安全性有着极高的要求。

传统的做法是在每个Controller方法中手动进行加解密操作,这会导致业务代码与安全代码严重耦合,不仅繁琐而且难以维护。本文将介绍一种更优雅的解决方案:利用Java的AOP(面向切面编程)技术,结合RSA非对称加密算法,实现对请求和响应的自动加解密,让开发者能够专注于核心业务逻辑。

一、RSA加密与AOP:安全与解耦的完美结合

RSA是一种非对称加密算法,它使用一对密钥:公钥和私钥。公钥可以公开给任何人,用于加密数据;私钥则必须严格保密,用于解密数据。在API对接场景中,通常的流程是:

  1. 服务端生成RSA密钥对,并将公钥分发给客户端。
  2. 客户端使用公钥加密敏感数据,然后发送给服务端。
  3. 服务端接收到请求后,使用自己的私钥进行解密,得到原始数据。
  4. 服务端处理完业务后,使用私钥对响应数据进行签名或加密,再返回给客户端。

AOP(面向切面编程)是Spring框架的核心特性之一,它允许我们将横切关注点(如日志、安全、事务)从核心业务逻辑中分离出来。通过将加解密逻辑封装在一个切面(Aspect)中,我们可以实现“无侵入式”的安全增强,所有标记了特定注解的接口都将自动具备加解密能力。
在这里插入图片描述

二、RSA工具类实现

首先,我们需要一个工具类来封装RSA的密钥生成、加密和解密操作。

package baodanbao.com.cn.security;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * RSA加密解密工具类
 * @author baodanbao.com.cn
 */
public class RSAUtil {

    private static final String ALGORITHM = "RSA";
    private static final int KEY_SIZE = 2048;

    // 用于缓存公钥和私钥,实际项目中应使用更安全的存储方式
    private static String publicKey;
    private static String privateKey;

    /**
     * 生成RSA密钥对
     */
    public static Map<String, String> generateKeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        PublicKey aPublic = keyPair.getPublic();
        PrivateKey aPrivate = keyPair.getPrivate();

        publicKey = Base64.getEncoder().encodeToString(aPublic.getEncoded());
        privateKey = Base64.getEncoder().encodeToString(aPrivate.getEncoded());

        Map<String, String> keyMap = new HashMap<>();
        keyMap.put("publicKey", publicKey);
        keyMap.put("privateKey", privateKey);
        return keyMap;
    }

    /**
     * 使用公钥加密
     */
    public static String encryptByPublicKey(String data, String publicKeyStr) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey aPublic = keyFactory.generatePublic(keySpec);

        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, aPublic);
        byte[] encryptedBytes = cipher.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    /**
     * 使用私钥解密
     */
    public static String decryptByPrivateKey(String encryptedData, String privateKeyStr) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(privateKeyStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PrivateKey aPrivate = keyFactory.generatePrivate(keySpec);

        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, aPrivate);
        byte[] decodedBytes = Base64.getDecoder().decode(encryptedData);
        byte[] decryptedBytes = cipher.doFinal(decodedBytes);
        return new String(decryptedBytes);
    }
}
三、定义加解密注解

接下来,我们定义两个注解,用于标记哪些接口需要进行请求解密和响应加密。

package baodanbao.com.cn.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 标记需要解密请求体的接口
 * @author baodanbao.com.cn
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DecryptRequest {
}
package baodanbao.com.cn.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 标记需要加密响应体的接口
 * @author baodanbao.com.cn
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptResponse {
}
四、实现AOP切面

这是整个方案的核心。我们创建一个切面,拦截所有标记了@DecryptRequest@EncryptResponse的方法,并在方法执行前后自动完成加解密操作。

package baodanbao.com.cn.aspect;

import baodanbao.com.cn.annotation.DecryptRequest;
import baodanbao.com.cn.annotation.EncryptResponse;
import baodanbao.com.cn.security.RSAUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 请求响应加解密切面
 * @author baodanbao.com.cn
 */
@Aspect
@Component
@Order(1) // 确保切面在其他切面之前执行
public class CryptoAspect {

    private final ObjectMapper objectMapper = new ObjectMapper();
    // 在实际项目中,私钥应从安全的配置中心或密钥管理服务中获取
    private final String privateKey = "YOUR_PRIVATE_KEY_HERE"; 

    /**
     * 环绕通知,处理请求解密和响应加密
     */
    @Around("@annotation(decryptRequest) || @annotation(encryptResponse)")
    public Object around(ProceedingJoinPoint joinPoint, DecryptRequest decryptRequest, EncryptResponse encryptResponse) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

        // 1. 处理请求解密
        if (decryptRequest != null) {
            // 从请求体中获取加密数据(此处简化,实际应从InputStream读取)
            String encryptedBody = request.getReader().lines().reduce("", (accumulator, actual) -> accumulator + actual);
            
            // 使用私钥解密
            String decryptedJson = RSAUtil.decryptByPrivateKey(encryptedBody, privateKey);
            
            // 将解密后的JSON字符串转换为方法参数(此处为简化示例,实际需更复杂的参数解析)
            // 这里假设请求体直接映射到第一个参数
            Object targetArg = objectMapper.readValue(decryptedJson, joinPoint.getSignature().getParameterTypes()[0]);
            joinPoint.getArgs()[0] = targetArg;
        }

        // 2. 执行目标方法
        Object result = joinPoint.proceed();

        // 3. 处理响应加密
        if (encryptResponse != null) {
            // 将响应对象转换为JSON字符串
            String jsonResponse = objectMapper.writeValueAsString(result);
            
            // 使用私钥加密(实际场景中,响应加密可能使用对称加密,或仅进行签名)
            // 此处为演示,仍使用RSA加密
            String encryptedResponse = RSAUtil.encryptByPublicKey(jsonResponse, RSAUtil.publicKey);
            
            // 将加密后的字符串包装成统一的响应格式
            Map<String, String> responseMap = new HashMap<>();
            responseMap.put("data", encryptedResponse);
            return responseMap;
        }

        return result;
    }
}
五、在Controller中使用

最后,在Controller中,我们只需简单地添加注解,即可享受自动加解密带来的便利。

package baodanbao.com.cn.controller;

import baodanbao.com.cn.annotation.DecryptRequest;
import baodanbao.com.cn.annotation.EncryptResponse;
import baodanbao.com.cn.model.OrderRequest;
import baodanbao.com.cn.model.OrderResponse;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * 订单Controller示例
 * @author baodanbao.com.cn
 */
@RestController
public class OrderController {

    @PostMapping("/api/order/create")
    @DecryptRequest
    @EncryptResponse
    public OrderResponse createOrder(@RequestBody OrderRequest orderRequest) {
        // 此处的orderRequest已经是解密后的对象
        System.out.println("收到订单请求: " + orderRequest.getUserId());

        // 处理业务逻辑...
        OrderResponse response = new OrderResponse();
        response.setOrderId("123456789");
        response.setStatus("SUCCESS");

        // 返回的response对象将被自动加密
        return response;
    }
}

通过这套方案,我们成功地将数据安全逻辑从业务代码中剥离,实现了高度的解耦和复用。所有对接俱美开放平台的API接口,都能以最小的侵入性获得企业级的数据安全保障。

本文著作权归 俱美开放平台 ,转载请注明出处!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值