一、接口签名的作用与实现要点
1. 为什么需要接口签名?
- 目的:防止请求伪造。
- 伪造风险:第三方可模拟合法请求,执行敏感操作(如转账)。
- 签名作用:确保请求来源真实、数据未被篡改。
2. 如何实现接口签名?
- 共享密钥:客户端与服务端约定一个保密的 secretKey。
- 生成签名:使用 secretKey + 请求体 + 其他参数(如 nonce、timestamp)通过安全算法(如 HMAC-MD5)生成签名。
- 传输签名:将签名放入请求头中发送。
- 服务端验证:服务端按相同规则重新计算签名,比对一致性。
3. 防止请求伪造
- 原理:攻击者无法获取 secretKey,无法生成有效签名,请求被拒绝。
4. 防止请求重放
- 定义:攻击者截获并重复发送旧请求,冒充合法用户。
- 解决方法:
- 使用唯一随机值
nonce,服务端记录已使用的 nonce(如 Redis),防止重复使用。
- 引入
timestamp,限定请求在时间窗口内有效(如 5 分钟)。
二、方案实战
1. AccountController账户控制类
@RestController
@Slf4j
public class AccountController {
@RequestMapping("/account/transfer")
public ResponseResult<String> transfer(@RequestBody TransferAccount request) {
log.info("转账成功:{}", JSONUtil.toJsonStr(request));
return ResponseResult.success("转账成功");
}
}
2. 自定义request包装类:可重用的主体请求包装器
public class ReusableBodyRequestWrapper extends HttpServletRequestWrapper {
@Getter
private byte[] requestBody;
private HttpServletRequest request;
public ReusableBodyRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
this.request = request;
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (null == this.requestBody) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(request.getInputStream(), baos);
this.requestBody = baos.toByteArray();
}
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}