当前位置: 代码网 > it编程>编程语言>Java > SpringBoot实现防篡改防重放的操作步骤

SpringBoot实现防篡改防重放的操作步骤

2025年07月08日 Java 我要评论
第一步:环境搭建——给你的接口项目打个底首先,我们需要准备好开发环境,并安装必要的依赖!1.1 创建springboot项目# 使用spring initializr快速创建项

第一步:环境搭建——给你的接口项目打个底

首先,我们需要准备好开发环境,并安装必要的依赖!

1.1 创建springboot项目

# 使用spring initializr快速创建项目
mvn archetype:generate -dgroupid=com.example -dartifactid=secureapi -darchetypeartifactid=maven-archetype-quickstart -dinteractivemode=false
cd secureapi

1.2 添加依赖

<dependencies>
    <!-- spring web -->
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-web</artifactid>
    </dependency>
    <!-- redis -->
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-data-redis</artifactid>
    </dependency>
    <!-- hmac加密 -->
    <dependency>
        <groupid>commons-codec</groupid>
        <artifactid>commons-codec</artifactid>
        <version>1.16.0</version>
    </dependency>
    <!-- lombok(简化代码) -->
    <dependency>
        <groupid>org.projectlombok</groupid>
        <artifactid>lombok</artifactid>
        <optional>true</optional>
    </dependency>
</dependencies>

1.3 配置redis

spring:
  redis:
    host: localhost
    port: 6379

第二步:传统方法——“裸奔接口”的惨痛教训

先来看看“零防护接口”的漏洞案例,对比之后你会更珍惜安全机制!

2.1 错误方法一:纯明文传输(失败案例)

// 未加密的接口示例
@restcontroller
public class usercontroller {
    @getmapping("/user")
    public string getuser(@requestparam string username) {
        return "hello, " + username; // 黑客可轻易修改username参数!
    }
}

2.2 错误方法二:无时间戳验证

// 未防重放的接口示例
@postmapping("/transfer")
public string transfermoney(@requestparam string amount) {
    // 黑客可重复发送请求,多次转账!
    return "transfer " + amount + "成功!";
}

疑问来了:为什么这两种方法不行?

  • 明文传输:参数易被篡改,攻击者可直接修改。
  • 无时间戳验证:攻击者可重放请求,导致重复操作。

第三步:推荐方法一——参数加密与签名:让接口“穿上防弹衣”!

用hmac-sha256实现参数签名,确保数据未被篡改!

3.1 客户端签名生成(javascript示例)

// 前端生成签名
function generatesign(params, secretkey) {
    // 1. 参数按字典序排序
    const sortedparams = object.keys(params)
        .sort()
        .map(key => `${key}=${params[key]}`)
        .join('&');
    // 2. 使用hmac-sha256生成签名
    const hmac = cryptojs.hmacsha256(sortedparams, secretkey);
    return hmac.tostring(cryptojs.enc.hex);
}

// 示例调用
const params = { username: 'alice', timestamp: date.now() };
const sign = generatesign(params, 'your-secret-key');

3.2 服务端签名验证(java代码)

@component
public class signaturevalidator {
    private final string secretkey = "your-secret-key";

    public boolean validatesignature(httpservletrequest request) {
        // 1. 获取请求参数和签名
        map<string, string> params = getparams(request);
        string sign = request.getheader("sign");
        // 2. 重新生成签名
        string sortedparams = params.entryset().stream()
            .sorted(comparator.comparing(map.entry::getkey))
            .map(entry -> entry.getkey() + "=" + entry.getvalue())
            .collect(collectors.joining("&"));
        string generatedsign = hmacutils.hmacsha256hex(secretkey, sortedparams);
        // 3. 对比签名
        return sign.equals(generatedsign);
    }

    private map<string, string> getparams(httpservletrequest request) {
        map<string, string> params = new hashmap<>();
        enumeration<string> paramnames = request.getparameternames();
        while (paramnames.hasmoreelements()) {
            string paramname = paramnames.nextelement();
            params.put(paramname, request.getparameter(paramname));
        }
        return params;
    }
}

3.3 过滤器集成

@component
public class signaturefilter implements filter {
    @autowired
    private signaturevalidator validator;

    @override
    public void dofilter(servletrequest request, servletresponse response, filterchain chain) {
        httpservletrequest req = (httpservletrequest) request;
        if (!validator.validatesignature(req)) {
            throw new runtimeexception("签名验证失败!参数被篡改");
        }
        chain.dofilter(request, response);
    }
}

第四步:推荐方法二——时间戳与redis防重放:让接口“时间刺客”无处遁形!

用时间戳+redis记录nonce,防止重复请求!

4.1 时间戳验证逻辑

public boolean validatetimestamp(httpservletrequest request) {
    long currenttime = system.currenttimemillis();
    long requesttime = long.parselong(request.getheader("timestamp"));
    // 允许时间差60秒
    return (currenttime - requesttime) <= 60_000;
}

4.2 redis记录nonce(唯一标识)

public boolean validatenonce(httpservletrequest request) {
    string nonce = request.getheader("nonce");
    // 使用redis记录已使用的nonce,有效期60秒
    string key = "nonce:" + nonce;
    if (redistemplate.haskey(key)) {
        return false; // 已存在,说明是重放请求
    }
    redistemplate.opsforvalue().set(key, "used", 60, timeunit.seconds);
    return true;
}

4.3 完整过滤器实现

@component
public class securityfilter implements filter {
    @autowired
    private redistemplate<string, string> redistemplate;

    @override
    public void dofilter(servletrequest request, servletresponse response, filterchain chain) {
        httpservletrequest req = (httpservletrequest) request;
        // 1. 验证签名
        if (!validatesignature(req)) {
            throw new runtimeexception("签名验证失败!");
        }
        // 2. 验证时间戳
        if (!validatetimestamp(req)) {
            throw new runtimeexception("时间戳超时!");
        }
        // 3. 验证nonce
        if (!validatenonce(req)) {
            throw new runtimeexception("重复请求!防重放失败");
        }
        chain.dofilter(request, response);
    }
}

第五步:推荐方法三——https加密传输:给接口“穿金戴银”!

用https加密传输,确保数据不被窃取!

5.1 配置https

server:
  port: 8443
  ssl:
    key-store: classpath:keystore.p12
    key-store-password: your-password
    key-store-type: pkcs12

5.2 生成密钥库(java命令)

keytool -genkeypair -alias mykey -keyalg rsa -keysize 2048 -storetype pkcs12 -keystore keystore.p12 -validity 365

第六步:实战案例——从零到有实现一个“银行转账接口”

整合所有技术,实现一个安全的转账接口!

6.1 接口定义

@restcontroller
public class transfercontroller {
    @postmapping("/transfer")
    public string transfer(@requestparam string amount, @requestparam string nonce) {
        // 实际转账逻辑(此处省略)
        return "转账" + amount + "元成功!";
    }
}

6.2 客户端调用示例(javascript)

const params = {
    amount: "1000",
    timestamp: date.now(),
    nonce: math.random().tostring(36).substr(2)
};
const sign = generatesign(params, 'your-secret-key');

fetch('/transfer', {
    method: 'post',
    headers: {
        'sign': sign,
        'timestamp': params.timestamp,
        'nonce': params.nonce
    },
    body: new urlsearchparams(params)
});

第七步:隐藏技巧——分布式环境下时间同步与nonce全局管理

用ntp同步时间和redis集群保证分布式部署!

7.1 ntp时间同步(linux命令)

sudo ntpdate pool.ntp.org

7.2 redis集群配置

spring:
  redis:
    cluster:
      nodes:
        - 192.168.1.100:6379
        - 192.168.1.101:6379

第八步:压力测试——让接口“吃鸡”!

最后,用压力测试验证你的安全机制是否扛得住百万级请求!

8.1 jmeter测试计划

<testplan>
    <threadgroup num_threads="1000">
        <httpsamplerproxy>
            <headermanager>
                <header>sign=your-sign</header>
                <header>timestamp=1623456789</header>
                <header>nonce=random-123</header>
            </headermanager>
            <path>/transfer</path>
        </httpsamplerproxy>
    </threadgroup>
</testplan>

从“裸奔接口”到“黑客退退退”,让安全机制“秒变”神器

经过这8个步骤的学习,我们不仅掌握了参数签名、时间戳防重放和https加密,还了解了如何在分布式环境下实现全局安全控制。无论是电商支付接口还是物联网控制接口,这些技巧都能让你的springboot项目像“钢铁侠”一样坚不可摧!

以上就是springboot防篡改防重放的操作步骤的详细内容,更多关于springboot防篡改防重放的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com