Springboot实战:基于HmacSHA256算法实现API签名验证

🧑 博主简介:历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。

在这里插入图片描述

在这里插入图片描述

Spring Boot 基于HmacSHA256算法实现API签名验证

在现代Web应用中,API的安全性至关重要。接口签名验证是一种常见的安全机制,用于确保请求的完整性和真实性。本文将详细介绍如何在Spring Boot应用中实现接口签名验证,并提供真实典型的代码示例。我们将使用HmacSHA256算法进行签名生成和验证。

HmacSHA256 算法简介

HmacSHA256 算法是一种消息认证码算法,它是 SHA-256 算法的变形版。通过使用密钥和散列函数,确保消息的认证性和完整性,防止消息被篡改。它广泛应用于网络安全领域,如 HTTPS 等协议。

HmacSHA256 算法具有安全性高实用性强易于实现灵活性高抗碰撞能力强等优点。在进行 HmacSHA256 算法运算时,需要传递密钥和数据两个参数,通过特定的方法将密钥转换成一个值,再与数据进行异或运算,并使用 SHA-256 哈希函数进行处理,得到一个 256 位的哈希值作为结果。

HmacSHA256 算法优点:

  1. 安全性高:通过使用密钥和散列函数,确保消息的认证性和完整性,防止消息被篡改。
  2. 实用性强:广泛应用于网络安全领域,如 HTTPS 等协议。
  3. 易于实现:实现过程相对简单,只需使用对应的加密库。
  4. 灵活性高:可将不同的散列函数和密钥组合使用,提高安全性。
  5. 抗碰撞能力强:具有较大的哈希值长度,减小了碰撞的概率。

一、接口签名验证原理

接口签名验证的核心思想是通过对请求参数进行加密生成签名,并将签名附加到请求中。服务器端接收到请求后,使用相同的算法对请求参数进行加密,并与请求中的签名进行比对。如果一致,则认为请求是合法的;否则,请求将被拒绝。

1.1 签名生成步骤

  1. 排序参数:将请求参数按字典序排序。
  2. 拼接参数:将排序后的参数按特定格式拼接成字符串。
  3. 加密签名:使用HmacSHA256算法对拼接后的字符串进行加密生成签名。
  4. 附加签名:将生成的签名附加到请求中。

1.2 签名验证步骤

  1. 获取请求参数:从请求中提取所有参数。
  2. 排序参数:将请求参数按字典序排序。
  3. 拼接参数:将排序后的参数按特定格式拼接成字符串。
  4. 加密签名:使用HmacSHA256算法对拼接后的字符串进行加密生成签名。
  5. 比对签名:将生成的签名与请求中的签名进行比对。

二、Spring Boot 实现接口签名验证

在Spring Boot中,我们可以通过自定义过滤器或拦截器来实现接口签名验证。以下是一个典型的实现示例。

2.1 创建签名工具类

首先,我们需要创建一个工具类来生成和验证签名。

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.Map;

public class SignUtil {

    private static final String SECRET_KEY = "your_secret_key"; // 密钥
    private static final String HMAC_SHA256 = "HmacSHA256";

    /**
     * 生成签名
     * @param params 请求参数
     * @return 签名
     */
    public static String generateSign(Map<String, String> params) {
        // 按字典序排序
        String[] sortedKeys = params.keySet().toArray(new String[0]);
        Arrays.sort(sortedKeys);

        // 拼接参数
        StringBuilder paramStr = new StringBuilder();
        for (String key : sortedKeys) {
            paramStr.append(key).append(params.get(key));
        }

        // 拼接密钥
        paramStr.append(SECRET_KEY);

        // 生成签名
        return Base64.getEncoder().encodeToString(hmacSha256(paramStr.toString().getBytes(StandardCharsets.UTF_8), SECRET_KEY.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 验证签名
     * @param params 请求参数
     * @param sign 请求中的签名
     * @return 签名是否有效
     */
    public static boolean verifySign(Map<String, String> params, String sign) {
        if (sign == null || sign.isEmpty()) {
            return false;
        }

        String generatedSign = generateSign(params);
        return generatedSign.equals(sign);
    }

    /**
     * 使用HmacSHA256算法生成签名
     * @param data 数据
     * @param key 密钥
     * @return 签名
     */
    private static byte[] hmacSha256(byte[] data, byte[] key) {
        try {
            Mac mac = Mac.getInstance(HMAC_SHA256);
            mac.init(new SecretKeySpec(key, HMAC_SHA256));
            return mac.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate HMAC SHA256", e);
        }
    }
}

2.2 创建拦截器

接下来,我们创建一个拦截器来拦截请求并进行签名验证。

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

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

@Component
public class SignInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取请求参数
        Map<String, String> params = new HashMap<>();
        Enumeration<String> paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            params.put(paramName, request.getParameter(paramName));
        }

        // 获取请求中的签名
        String sign = request.getParameter("sign");

        // 验证签名
        if (!SignUtil.verifySign(params, sign)) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().write("Invalid signature");
            return false;
        }

        return true;
    }
}

2.3 配置拦截器

最后,我们需要在Spring Boot中配置拦截器。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private SignInterceptor signInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(signInterceptor)
                .addPathPatterns("/**"); // 拦截所有路径
    }
}

三、Maven 依赖

为了确保以上代码示例能够正常运行,我们需要在 pom.xml 中添加以下依赖:

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Apache Commons Codec -->
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.15</version>
    </dependency>
</dependencies>

四、测试代码示例

为验证api签名功能正常,特意编写了一个简单的测试控制器和测试代码。

4.1 测试控制器

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class TestController {

    @GetMapping("/test")
    public String test(@RequestParam Map<String, String> params) {
        return "Request is valid";
    }

    @GetMapping("/generateSign")
    public String generateSign(@RequestParam Map<String, String> params) {
        return SignUtil.generateSign(params);
    }
}

4.2 测试代码

我们可以使用Postman或其他HTTP客户端工具来测试接口。

  1. 生成签名

    • 请求URL:http://localhost:8080/generateSign
    • 请求参数:param1=value1&param2=value2
    • 获取生成的签名。
  2. 验证签名

    • 请求URL:http://localhost:8080/test
    • 请求参数:param1=value1&param2=value2&sign=生成的签名
    • 验证返回结果是否为Request is valid

五、总结

通过以上步骤,我们成功在Spring Boot应用中实现了接口签名验证。这种机制可以有效防止请求被篡改和伪造,提高API的安全性。希望本文的示例代码和解释能够帮助你更好地理解和应用接口签名验证。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月下独码

你的打赏是我精心创作的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值