🛡️ Spring Boot 对接 Amazon SP-API 的工程化实践
------ 授权封装、SDK 分层与 AAD 加密一致性设计
在对接 Amazon SP-API 的过程中,"接口能跑"只是第一步 。
真正进入长期维护阶段后,你会迅速遇到几个现实问题:
- SP-API SDK 多、接口多,代码开始重复
- 授权信息需要加密,但解密失败问题偶发且致命
- AAD(Additional Authenticated Data)一旦不一致,历史数据将全部失效
- SDK 调用、业务逻辑、安全逻辑混在一起,难以维护
本文将结合一次真实的工程实践,完整讲解我是如何重构 SP-API 授权与 SDK 接入层的。
🎯 设计目标
在开始之前,先明确几个设计目标:
- 🔹 SDK 调用与业务逻辑彻底解耦
- 🔹 授权信息安全存储,避免 AAD 不一致导致解密失败
- 🔹 支持多个 SP-API(Sellers / Orders / Reports ...)
- 🔹 一次解密,多 API 复用
- 🔹 可扩展到 Temu / TikTok 等其他平台
🧱 一、整体分层设计
最终我采用了下面这套结构:
Controller
↓
Service
↓
Manager(SP-API SDK)
↓
Amazon SDK
每一层只做一件事。
🧩 二、核心模块职责划分
1️⃣ Service 层 ------ 业务裁判
-
决定什么时候校验授权
-
决定授权失败后的业务处理
-
不直接接触 SDK
AuthCheckService
2️⃣ Manager 层 ------ SDK 适配器
-
只负责调用 Amazon SDK
-
不操作数据库
-
不解密、不拼 AAD
-
不关心 storeCode
SellerManager
OrdersManager
ReportsManager
3️⃣ Factory 层 ------ SDK 构建中心
SpApiAuthContextFactory
SpApiClientFactory
- 统一解密
- 统一 endpoint 选择
- 统一 SDK Client 创建
4️⃣ Crypto 层 ------ 安全核心(重点)
CryptoKeyProvider
CryptoService
CryptoFieldEnum
CryptoUtil
👉 所有加解密规则只能从这里走
🔐 三、为什么 AAD 一定要统一?
在 SP-API 授权信息加密时,我使用了 AEAD 算法:
CryptoUtil.encrypt(plainText, masterKey, aad);
其中:
masterKey:系统主密钥aad:附加认证数据(AAD)
❗ AAD 的致命特点
加密和解密时的 AAD 必须完全一致
哪怕只是:
storeCode:clientId
storeCode:client_id
都会导致:
- ❌ 解密失败
- ❌ 历史数据全部不可用
- ❌ 无法补救
🧠 四、正确做法:字段级 AAD 枚举化
✅ 定义 AAD 唯一来源
@Getter
@AllArgsConstructor
public enum CryptoFieldEnum {
SP_API_CLIENT_ID("clientId"),
SP_API_CLIENT_SECRET("clientSecret"),
SP_API_REFRESH_TOKEN("refreshToken");
private final String field;
public String aad(String storeCode) {
return storeCode + ":" + field;
}
}
🔒 枚举一旦上线,不允许修改值
🔧 五、CryptoService:唯一加解密入口
@Component
public class CryptoService {
@Resource
private CryptoKeyProvider cryptoKeyProvider;
public String encryptSpApiField(
String plainText,
String storeCode,
CryptoFieldEnum field
) {
return CryptoUtil.encrypt(
plainText,
cryptoKeyProvider.getMasterKey(),
field.aad(storeCode)
);
}
public String decryptSpApiField(
String cipherText,
String storeCode,
CryptoFieldEnum field
) {
return CryptoUtil.decrypt(
cipherText,
cryptoKeyProvider.getMasterKey(),
field.aad(storeCode)
);
}
}
📌 业务代码再也不需要知道 AAD 是什么
🧩 六、SpApiAuthContext:一次解密,多 API 复用
public class SpApiAuthContext {
private final LWAAuthorizationCredentials lwaCredentials;
private final String endpoint;
}
创建 Context
SpApiAuthContext ctx = authContextFactory.create(spStoreAuth);
使用 Context
sellerManager.getMarketplaceParticipations(ctx);
ordersManager.listOrders(ctx);
🏭 七、ClientFactory:统一构建 SDK Client
@Component
public class SpApiClientFactory {
public SellersApi createSellersApi(SpApiAuthContext ctx) {
return new SellersApi.Builder()
.lwaAuthorizationCredentials(ctx.getLwaCredentials())
.endpoint(ctx.getEndpoint())
.build();
}
}
🧭 八、类依赖关系图(核心)
┌───────────────────────────┐
│ AuthCheckService │
└─────────────┬─────────────┘
↓
┌───────────────────────────┐
│ SpApiAuthContextFactory │
└─────────────┬─────────────┘
↓
┌───────────────────────────┐
│ CryptoService │
└─────────────┬─────────────┘
↓
┌───────────────────────────┐
│ CryptoKeyProvider │
└───────────────────────────┘
┌───────────────────────────┐
│ SellerManager │
└─────────────┬─────────────┘
↓
┌───────────────────────────┐
│ SpApiClientFactory │
└─────────────┬─────────────┘
↓
┌───────────────────────────┐
│ Amazon SDK │
└───────────────────────────┘
✅ 九、这种设计带来的收益
| 收益 | 说明 |
|---|---|
| 🔒 安全 | AAD 绝对一致,历史数据安全 |
| 🔁 复用 | 一次解密,多 API |
| 🧪 测试 | Manager 可轻松 Mock |
| 🧩 扩展 | 新平台只加 Manager |
| 🧠 清晰 | 每层职责单一 |
📌 十、总结
在 SP-API 对接中,真正难的不是接口调用,而是长期演进能力。
本文通过:
- SDK 分层
- 授权上下文封装
- AAD 统一治理
构建了一套安全、可维护、可扩展的 SP-API 接入方案。
如果你也在做多平台对接(Amazon / Temu / TikTok),
这套结构可以直接复用。
