SpringBoot拦截器实现API安全验证的示例详解

 更新时间:2025年11月24日 08:20:44   作者:风象南  
在开放平台和第三方集成的项目中,如何确保 API 调用的安全性和可靠性是一个重要课题,本文将详细介绍如何基于 Spring Boot 拦截器和 HMAC-SHA256 算法,构建一套轻量级但足够安全的 API 验签机制,希望对大家有所帮助

前言

在开放平台和第三方集成的项目中,如何确保 API 调用的安全性和可靠性是一个重要课题。特别是对于没有用户登录场景的系统间调用,传统的session或token认证方式并不适用,数字签名技术就成为了一种理想的选择。

这种验证方式在开放平台、SaaS服务、微服务间调用等场景下特别实用,能够有效验证请求来源的合法性,防止参数被篡改和重放攻击。

本文将详细介绍如何基于 Spring Boot 拦截器和 HMAC-SHA256 算法,构建一套轻量级但足够安全的 API 验签机制。

什么是数字签名

数字签名是一种用于验证数据完整性和真实性的技术手段。在 API 调用中,数字签名通过以下方式保障安全:

  • 1. 身份验证:确认请求方身份的合法性
  • 2. 数据完整性:确保请求参数在传输过程中未被篡改
  • 3. 防止重放攻击:通过时间戳等机制防止请求被重复使用

整体设计

设计思路

我们的验签机制基于以下核心思想:

  • 无侵入性:通过 Spring Boot 拦截器实现,业务代码无需改动
  • 算法安全性:采用业界成熟的 HMAC-SHA256 算法
  • 配置灵活:支持多客户端、多密钥管理

架构流程图

客户端请求 → 生成签名 → 发送请求 → 拦截器验证 → 业务处理
     ↓              ↓           ↓           ↓           ↓
  [参数整理]    [HMAC加密]  [携带签名头]  [验签逻辑]  [通过/拒绝]
     ↓              ↓           ↓           ↓           ↓
   参数排序    +时间戳加密   X-API-Key    密钥匹配    正常响应
     ↓              ↓           ↓           ↓           ↓
   拼接参数    Base64编码   X-Timestamp  时间戳验证   或返回401
     ↓              ↓           ↓           ↓
   待签字符串    完整签名     X-Signature  签名对比

HMAC-SHA256 签名原理

HMAC(Hash-based Message Authentication Code,基于哈希的消息认证码)结合了哈希函数和密钥,提供了一种安全高效的消息认证方式。

签名生成算法

签名 = Base64(HMAC-SHA256(时间戳 + 排序后的请求参数, 密钥))

算法步骤

1. 参数标准化:将所有请求参数按字典序排序

2. 数据拼接:将时间戳和排序后的参数按规则拼接

3. 签名运算:使用密钥对拼接字符串进行 HMAC-SHA256 运输

4. 编码转换:对加密结果进行 Base64 编码生成最终签名

核心组件实现

1. 签名工具类

签名工具类是整个机制的核心,负责签名的生成和验证逻辑

public class SignatureUtil {

    /**
     * 生成签名
     * 签名算法:Base64(HMAC-SHA256(timestamp + sortedParams, secret))
     */
    public static String generateSignature(Map<String, Object> params,
                                         String timestamp, String secret) {
        // 参数排序并拼接
        String sortedParams = sortParams(params);
        String dataToSign = timestamp + sortedParams;

        // HMAC-SHA256加密并Base64编码
        HMac hmac = new HMac(HmacAlgorithm.HmacSHA256, secret.getBytes(StandardCharsets.UTF_8));
        byte[] digest = hmac.digest(dataToSign);
        return Base64.getEncoder().encodeToString(digest);
    }

    /**
     * 验证时间戳有效性(防重放攻击)
     */
    public static boolean validateTimestamp(String timestamp, long tolerance) {
        long requestTime = Long.parseLong(timestamp);
        long currentTime = System.currentTimeMillis() / 1000;
        return Math.abs(currentTime - requestTime) <= tolerance;
    }
}

2. 拦截器

拦截器负责对所有受保护接口进行统一的签名验证

@Component
public class SignatureValidationInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 1. 获取签名头信息
        String timestamp = request.getHeader("X-Timestamp");
        String signature = request.getHeader("X-Signature");
        String apiKey = request.getHeader("X-Api-Key");

        // 2. 验证必要参数
        if (!StringUtils.hasText(timestamp) || !StringUtils.hasText(signature) || !StringUtils.hasText(apiKey)) {
            return writeErrorResponse(response, "Missing required signature headers");
        }

        // 3. 验证时间戳(防重放攻击)
        if (!SignatureUtil.validateTimestamp(timestamp, timeTolerance)) {
            return writeErrorResponse(response, "Invalid timestamp");
        }

        // 4. 获取密钥并验证签名
        String secret = securityProperties.getApiSecret(apiKey);
        if (secret == null || !SignatureUtil.verifySignature(extractParams(request), timestamp, secret, signature)) {
            return writeErrorResponse(response, "Invalid signature");
        }

        return true; // 验证通过,继续处理请求
    }
}

3. 配置类(WebMvcConfig)

配置类负责将拦截器集成到 Spring Boot 的请求处理链中:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(signatureValidationInterceptor)
                .addPathPatterns("/api/**")              // 拦截所有API请求
                .excludePathPatterns("/api/public/**");  // 排除公开接口
    }
}

客户端调用示例

请求头设置

客户端需要在请求头中包含三个必要字段:

  • X-Api-Key:客户端标识
  • X-Timestamp:当前时间戳(秒级)
  • X-Signature:生成的签名

完整调用流程

// 1. 准备请求参数
Map<String, Object> params = new HashMap<>();
params.put("userId", "12345");
params.put("type", "profile");

// 2. 生成签名
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String signature = SignatureUtil.generateSignature(params, timestamp, "your-secret");

// 3. 设置请求头并发送请求
Headers headers = new Headers();
headers.set("X-Api-Key", "client1");
headers.set("X-Timestamp", timestamp);
headers.set("X-Signature", signature);

// GET /api/protected/data?userId=12345&type=profile

安全性设计要点

1. 防重放攻击

  • 时间戳验证:服务端验证时间戳的有效性(默认容忍度5分钟)
  • 唯一性保证:相同参数在不同时间戳下生成不同签名
  • 配置灵活:可根据业务需求调整容忍时间

2. 密钥管理

  • 多客户端支持:每个客户端使用独立的 API Key 和密钥
  • 配置化管理:通过配置文件或其他存储组件统一管理密钥映射
  • 定期轮换:建议定期更换密钥以提升安全性

3. 日志审计

  • 请求日志:记录验证失败的关键信息(不包含完整签名)
  • IP追踪:记录客户端真实IP地址
  • 安全预警:异常签名验证触发告警机制

安全性增强建议

1. 传输层安全

  • HTTPS强制:所有API请求必须通过HTTPS传输
  • 证书验证:启用双向证书认证增加安全性
  • 协议升级:及时更新SSL/TLS协议版本

2. 密钥管理优化

// 生成安全密钥(32位随机字符串)
public static String generateSecureKey() {
    String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    SecureRandom random = SecureRandom.getInstanceStrong();
    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < 32; i++) {
        sb.append(chars.charAt(random.nextInt(chars.length())));
    }

    return sb.toString();
}

3. 请求限流

建议结合 Redis 实现请求限流,防止破解:

// 简限流逻辑示例
String rateLimitKey = "api_limit:" + apiKey;
long count = redisTemplate.opsForValue().increment(rateLimitKey);
if (count > 100) { // 每分钟限制100次请求
    return writeErrorResponse(response, "Too many requests");
}

测试验证

正常流程测试

# 使用curl测试(需要先根据参数生成签名)
timestamp=$(date +%s)
signature=$(生成签名逻辑)
curl -X GET "http://localhost:8080/api/protected/data?userId=12345" \
  -H "X-Api-Key: client1" \
  -H "X-Timestamp: $timestamp" \
  -H "X-Signature: $signature"

异常场景测试

  • 签名错误:修改参数但不更新签名
  • 时间戳过期:使用过期的时间戳
  • 密钥错误:使用错误的API Key
  • 缺少头信息:缺少必要的请求头

总结

通过 Spring Boot 拦截器和 HMAC-SHA256 算法,我们实现了一套完整且实用的 API 签名验证方案。这套机制有效解决了系统间调用的安全问题,而且对现有代码几乎零侵入,直接复用即可。

在实际项目中,你可以根据具体需求调整时间戳容忍度、密钥管理策略等配置,实现灵活的安全控制。

仓库地址:https://github.com/yuboon/java-examples/tree/master/springboot-api-signature

到此这篇关于SpringBoot拦截器实现API安全验证的示例详解的文章就介绍到这了,更多相关SpringBoot API 验签内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅谈Java中replace与replaceAll区别

    浅谈Java中replace与replaceAll区别

    这篇文章主要介绍了Java中replace与replaceAll区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • Java中的动态代理原理及实现

    Java中的动态代理原理及实现

    这篇文章主要介绍了Java中的动态代理原理及实现,动态是相对于静态而言,何为静态,即编码时手动编写代理类、委托类,而动态呢,是不编写具体实现类,等到使用时,动态创建一个来实现代理的目的,需要的朋友可以参考下
    2023-12-12
  • JPA多数据源分布式事务处理方案

    JPA多数据源分布式事务处理方案

    这篇文章主要为大家介绍了JPA多数据源分布式事务处理的两种事务方案,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-02-02
  • Java通过SSLEngine与NIO实现HTTPS访问的操作方法

    Java通过SSLEngine与NIO实现HTTPS访问的操作方法

    这篇文章主要介绍了Java通过SSLEngine与NIO实现HTTPS访问,需要在Connect操作、Connected操作、Read和Write操作中加入SSL相关的处理即可,需要的朋友可以参考下
    2021-08-08
  • springboot如何重定向外部网页

    springboot如何重定向外部网页

    这篇文章主要介绍了springboot如何重定向外部网页,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • Mybatis配置文件之动态SQL配置备忘录

    Mybatis配置文件之动态SQL配置备忘录

    这篇文章主要介绍了Mybatis配置文件之动态SQL配置备忘录的相关资料,需要的朋友可以参考下
    2017-05-05
  • Java简单操作k8s,创建Deployment、Service、访问Nginx实践

    Java简单操作k8s,创建Deployment、Service、访问Nginx实践

    文章介绍了使用Maven依赖并初始化KubernetesClient客户端,通过配置kubeconfig文件连接Kubernetes集群,编写配置类将KubernetesClient初始化为Bean,并测试连接,最后,提供了一个访问部署在Kubernetes中的Nginx服务的示例,以及后续扩展思路
    2026-04-04
  • 使用jps命令查看Java进程的详细指南

    使用jps命令查看Java进程的详细指南

    jps是Java开发者和系统管理员的得力助手,它简化了Java进程监控的过程,使得快速检查应用运行状态变得轻而易举,在Java开发和运维场景中,jps是一个非常实用的命令行工具,本文介绍了如何有效地使用 jps命令来查看Java进程的详细指南,需要的朋友可以参考下
    2024-10-10
  • springboot自定义拦截器的方法

    springboot自定义拦截器的方法

    这篇文章主要为大家详细介绍了springboot自定义拦截器的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • java中添加按钮并添加响应事件的方法(推荐)

    java中添加按钮并添加响应事件的方法(推荐)

    下面小编就为大家带来一篇java中添加按钮并添加响应事件的方法(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04

最新评论