系统接口参数加解密
系统接口参数加解密
一、 背景与加密策略
在当前主流的微服务架构中,前后端分离的开发模式十分普遍。这种模式虽然提高了开发效率和灵活性,但也使得后端的API接口直接暴露于外部网络,带来了参数被截获或篡改的安全风险。为了保障数据传输的安全性与完整性,对接口参数进行加密处理是至关重要的。
本项目在综合考量了不同加密算法的优劣后,最终选择了 RSA非对称加密 方案。
加密算法简介
- 对称加密 (如AES): 使用同一密钥进行加解密。其优势在于处理速度快,适合对大量数据进行加密。然而,其主要挑战在于密钥的安全分发与管理,一旦密钥泄露,信息安全将荡然无存。
- 非对称加密 (如RSA): 使用一对密钥——公钥和私钥。公钥对外公开,用于加密数据或验证签名;私钥由持有者秘密保管,用于解密数据或生成签名。这种机制完美地解决了对称加密中的密钥分发难题,并能同时实现数据加密和数字签名,提供了更高级别的安全保障。尽管其加解密速度相较于对称加密更慢,但对于接口参数这类数据量不大的场景,其安全性优势更为突出。
本项目利用RSA的特性,由前端负责对请求数据进行加密和签名,而后端网关则负责解密和验签,确保通信安全。
二、 基础配置与管理
为了支持多渠道、多平台的接入(例如小程序、Web端、移动客户端等),系统设计了一套灵活的渠道化配置机制。
1. 渠道数据配置
系统通过唯一的渠道编码 (code
) 来区分不同的调用方。每个渠道都拥有一套独立的加密和签名密钥。这些配置信息通过API接口(详见ApiFox中的base-data
模块)进行管理,并持久化存储在数据库的channel_data
表中。
数据表结构 (channel_data
)
CREATE TABLE `channel_data` (
`id` bigint(64) NOT NULL COMMENT 'id',
`name` varchar(50) DEFAULT NULL COMMENT '名称',
`code` varchar(50) NOT NULL COMMENT '编码',
`introduce` varchar(500) DEFAULT NULL COMMENT '介绍描述',
`sign_public_key` text NOT NULL COMMENT 'rsa签名公钥',
`sign_secret_key` text NOT NULL COMMENT 'rsa签名秘钥',
`aes_key` text COMMENT 'aes秘钥',
`data_public_key` text COMMENT 'rsa参数公钥',
`data_secret_key` text COMMENT 'rsa参数私钥',
`token_secret` text NOT NULL COMMENT 'token秘钥',
`status` int(1) DEFAULT '1' COMMENT '状态 1:启用 0:禁用',
`edit_time` datetime DEFAULT NULL COMMENT '编辑时间',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `code_IDX` (`code`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='渠道基础数据信息';
从表结构中可以看出,系统为签名验证和业务数据加解密分别准备了两套独立的RSA公私钥(sign_*
和 data_*
字段),以实现更强的安全隔离。
2. 高效的缓存机制
为避免每次请求都查询数据库,提高网关处理效率,系统在后台新增或更新渠道数据时,会同步将配置信息缓存到Redis中。 网关在处理请求时,会优先从Redis中根据code
快速获取对应渠道的密钥等配置信息。
以下是后台服务将数据写入Redis缓存的逻辑示例:
/**
* 将渠道配置数据存入Redis
*/
private void addRedisChannelData(ChannelData channelData){
GetChannelDataVo getChannelDataVo = new GetChannelDataVo();
BeanUtils.copyProperties(channelData, getChannelDataVo);
// 使用包装好的Redis Key模板,将数据存入
redisCache.set(RedisKeyWrap.createRedisKey(RedisKeyEnum.CHANNEL_DATA, getChannelDataVo.getCode()), getChannelDataVo);
}
三、 接口调用与加解密流程
1. 通用请求格式
所有需要安全验证的接口调用,均需遵循以下格式:
- 请求方式:
POST
- 请求体:
JSON
- 必需参数:
code
: 渠道编码,用于标识调用方身份。businessBody
: 经过处理的业务参数体。sign
: 基于参数生成的签名。
2. 具体实现方案
系统支持多种调用方式,以适应不同场景的需求。
方案一:跳过安全验证(调试模式)
在特定情况下(如开发或调试阶段),可以临时绕过签名和加密流程。只需在请求头中加入 no_verify = true
即可。 此时,businessBody
可以直接传递未经任何处理的原始JSON业务参数。
示例请求:
{
"id": "111"
}
方案二:V1 - 签名验证(默认模式)
这是标准的默认安全模式,主要用于防止参数被篡改。
- 前端处理: 将所有业务参数和基础参数(
code
和businessBody
的字符串值)拼接起来,使用渠道对应的 RSA签名私钥 生成sign
。 - 网关处理: 网关接收到请求后,根据
code
参数从缓存中查询出对应的 RSA签名公钥,对接收到的参数和sign
进行验签。
示例请求:
{
"code": "1234",
"businessBody": "{\"id\":\"1111\",\"sleepTime\":10}",
"sign": "JdTXuqMTCMGmM5zs7LoHwcEwFS5HQo/9bttao0GAAdoI/MpUpg7Eb5RN3Tmm4QT6FZVdJGVLqK48QKBAhJUlrBA8D14SJj7teMtPGboSxJ475+rGvgdycQbGKf7o40YBXwJGJeOG6xNJz913+Z8Zf/R9Sbd9gjF1QBXvSZy5i/sTNxhDOGydhLetInUcC/iMsqzoCk4e9MKltUSf4rQV4LQ0E171n93DtKLI4RZ9gPRzTBT7tPkpuPZ2GoJ5pJTQiNcjiDDYtHBPBeemrXtqumDblIJJuOBrcQk+1sYicQFy9ZQY1PAMoHjCTCPKNxUAULsodpXEj1TYJUl+q2jTwg=="
}
方案三:V2 - 参数加密并签名(高安全模式)
对于安全级别要求更高的接口,可以启用此模式,同时实现数据保密性和抗篡改性。
- 启用方式: 在请求头中添加
encrypt = v2
。 - 核心机制: 此模式下,签名和加解密使用两套不同的RSA密钥对。
- 前端处理:
- 使用 RSA数据公钥 对原始业务参数(
businessBody
的内容)进行加密。 - 将 未加密 的原始业务参数和基础参数进行拼接,然后使用 RSA签名私钥 生成
sign
。 - 最终发送的请求中,
businessBody
是加密后的密文,而sign
是对原始数据生成的签名。
- 使用 RSA数据公钥 对原始业务参数(
- 网关处理:
- 验签: 网关首先使用 RSA数据私钥 将接收到的
businessBody
解密,得到原始业务参数。 - 然后,用解密后的原始业务参数和请求中的其他参数,配合 RSA签名公钥 进行验签。
- 响应加密: 在接口将处理结果返回给前端时,网关会使用 RSA数据公钥 对返回数据进行加密,前端需要用对应的 RSA数据私钥 进行解密后才能获取真实数据。
- 验签: 网关首先使用 RSA数据私钥 将接收到的
示例请求 (高安全模式):
{
"code": "1234",
"businessBody": "SWwqEci1ci3Dog/iIKXirjirt+Va01RO2zlcBjPkpFBx1y2LPbKdgfLlGc+QnCgTXjbp/KkqFdqCcFBTJstdhy7+RNzxESBXKuuMlUwyXIPpNyIgLmSyu7Saoy2VbHw1Z2pXYTNdf7u3CL4MRuVjLszN6OY4qQozQAlp87sek2QLvh+wyi0csbXqJgvDDlh2LfpcFe7ycSxMDcTkYVOAqbHtXXzLI/I3W1KHcErHCZVuc+f6tk7Y4iBnR6MeFeqvOeziSBZVUBAQHlKr9Bm6fY1xHYx5NLavS8q/S4USwenntlG37J6Tb09D0+KahtAcguxLqTikbVxrcWpYv/tWRw==",
"sign": "EfxdpQB8GGWbQFNA1hBTnHhf4nWJerIsK+6NcuIptuzzooWgL+sFmGu9J9kG3hptiWRqkhW6DPDDRmfmUblvMuX9tC0jHoTnSeZBDkFi7+IJ+fdPg5iyi7+XHBCcR7pVilJgvjHXsDMjN3oaF1k4I56L5fYCfvcgSi6VQoGt+dB0kd4zWHWEGOu1c/TrowYCFaHElVq1fXPEd7dglbF2g4qHE8yrfELt6NXfO0K+P9elqB8NGnRcBQeM0d37+nQrCxOY5Mml2AKANs0UkLxZfrbqc95MjCWvDdRiQGGJnCIL1ZtMTsopgB2nGo8yMoqFVn4eE6G6wSFu1z1e5q4nQ=="
}