如何使用Spring Boot实现接口时间戳鉴权

 更新时间:2025年06月09日 10:01:41   作者:weixin_43833540  
这篇文章主要为大家详细介绍了如何使用Spring Boot实现接口时间戳鉴权,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴要了解下

Spring Boot实现接口时间戳鉴权,签名(sign)和时间戳(ts)放入请求头(Header)。

一、请求头参数设计

参数名类型说明
tsLong13位时间戳(Unix毫秒值),必填,标识请求有效期
signString签名值,必填,用于校验请求合法性

二、签名算法调整(Header版)

1. 构造原始字符串

str = key + url_encode(path) + ts

path:请求的URL路径部分(不含查询参数和域名),例如:/api/v1/user。

url_encode(path):需对路径中的特殊字符进行URL编码(如中文、/等无需编码,但空格需转义为%20)。

2. 生成签名 SIGN

sgin = md5(S).toLowerCase()

结果转换为小写字符串,与请求头中的sign对比校验。

三、Spring Boot实现流程

1. 创建拦截器(Interceptor)

用于拦截请求,校验ts和sign的合法性。

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Objects;

public class AuthInterceptor implements HandlerInterceptor {
    private final String secretKey; // 从配置文件获取密钥,例如application.properties
    private final long maxClockSkew = 60 * 1000; // 时间窗口:60秒(允许客户端与服务端的时间误差)

    public AuthInterceptor(String secretKey) {
        this.secretKey = secretKey;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 1. 校验时间戳是否存在
        String ts = request.getHeader("ts");
        if (StringUtils.isEmpty(ts)) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.setContentType("application/json");
            // 返回错误信息:缺少时间戳
            return false;
        }

        // 2. 校验时间戳格式及有效性
        long ts;
        try {
            ts = Long.parseLong(ts );
        } catch (NumberFormatException e) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            // 返回错误信息:时间戳格式错误
            return false;
        }
        long currentTime = System.currentTimeMillis();
        if (currentTime - ts > maxClockSkew|| ts - currentTime > maxClockSkew) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            // 返回错误信息:请求已过期
            return false;
        }

        // 3. 校验签名是否存在
        String sign= request.getHeader("sign");
        if (StringUtils.isEmpty(sign)) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            // 返回错误信息:缺少签名
            return false;
        }

        // 4. 构造原始签名字符串并生成签名
        String path = request.getRequestURI(); // 获取路径,例如"/api/v1/user"
        String encodedPath = URLEncoder.encode(path, StandardCharsets.UTF_8.toString())
                .replace("+", "%20") // 处理空格编码差异(URLEncoder默认用+,此处统一为%20)
                .replace("%7E", "~"); // 保留波浪线~的原始格式
        String rawString = secretKey + encodedPath + tsHeader;

        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(rawString.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02x", b)); // 转换为小写16进制字符串
            }
            String generatedSign = sb.toString();

            // 5. 对比签名
            if (!generatedSign.equals(sign)) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                // 返回错误信息:签名校验失败
                return false;
            }
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return false;
        }

        return true; // 校验通过,允许请求继续
    }
}

2. 配置拦截器(WebMvcConfigurer)

将拦截器注册到Spring Boot的拦截器链中,指定需要鉴权的接口路径。

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 {
    private final String secretKey; // 从配置文件获取密钥,例如通过@Value注入

    public WebConfig(String secretKey) {
        this.secretKey = secretKey;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        AuthInterceptor authInterceptor = new AuthInterceptor(secretKey);
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/api/v1/**") // 需要鉴权的接口路径(如/api/v1下所有接口)
                .excludePathPatterns("/api/v1/public/**"); // 无需鉴权的公共接口(可选)
    }
}

3. 配置文件(application.properties)

# 密钥(建议从环境变量或配置中心获取,而非硬编码)
auth.secret-key=your_secret_key_here

四、客户端请求示例(以Postman为例)

1. 请求头参数

名称
ts1686048000000(当前时间戳,13位Long)
sign计算得到的签名值(如e10adc3949ba59abbe56e057f20f883e)

2. 签名计算步骤(JavaScript示例)

function generateSignature(key, path, ts) {
    const encodedPath = encodeURIComponent(path).replace(/[!'()*]/g, encodeURIComponent); // 严格编码特殊字符
    const rawString = key + encodedPath + ts;
    const md5 = require('crypto-js/md5'); // 需要安装crypto-js库
    return md5(rawString).toString().toLowerCase();
}

// 示例调用
const key = 'your_secret_key';
const path = '/api/v1/user'; // 接口路径
const ts = Date.now(); // 当前时间戳(13位)
const sign = generateSignature(key, path, ts);
console.log(sign); // 输出签名值

到此这篇关于如何使用Spring Boot实现接口时间戳鉴权的文章就介绍到这了,更多相关Spring Boot接口时间戳鉴权内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot 根据配置决定服务(集群、单机)是否使用某些主件的操作代码

    Spring Boot 根据配置决定服务(集群、单机)是否使用某些主件的操作代码

    这篇文章主要介绍了Spring Boot根据配置决定服务(集群、单机)是否使用某些主件,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-04-04
  • Mybatis中返回主键值方式

    Mybatis中返回主键值方式

    这篇文章主要介绍了Mybatis中返回主键值方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • 从面试中的问题分析ThreadLocal

    从面试中的问题分析ThreadLocal

    这篇文章主要介绍了从面试中的问题分析ThreadLocal,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,下面我们来一起学习一下吧
    2019-06-06
  • spring中自动注入注解的实现方式

    spring中自动注入注解的实现方式

    在Spring框架中,AutowiredAnnotationBeanPostProcessor负责处理@Autowired和@Value注解,实现依赖注入,首先通过TypeMappedAnnotations获取注解,并根据注解属性构建InjectionMetadata,存入缓存
    2024-09-09
  • Java服务调用失败报Service com.oneinfinite.adflow.api.service.TestService未找到的解决方法

    Java服务调用失败报Service com.oneinfinite.adflow.api.service.T

    在Java开发中,服务调用是常见的操作,尤其是在微服务架构中,然而,服务调用过程中可能会遇到各种问题,下面我们来看看如何解决Service com.oneinfinite.adflow.api.service.TestService with version 0.0.0 not found的问题吧
    2025-03-03
  • mybatis-plus在yml中配置详解

    mybatis-plus在yml中配置详解

    本文主要介绍了mybatis-plus在yml中配置详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • Mybatis-Plus动态表名的实现示例

    Mybatis-Plus动态表名的实现示例

    面对复杂多变的业务需求,动态表名的处理变得愈发重要,本文主要介绍了Mybatis-Plus动态表名的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-07-07
  • Java实现五子棋游戏的完整代码

    Java实现五子棋游戏的完整代码

    这篇文章主要为大家详细介绍了Java实现五子棋游戏的完整代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-10-10
  • Java Web十条开发实用小知识

    Java Web十条开发实用小知识

    这篇文章主要介绍了Java Web十条开发实用小知识的相关资料,需要的朋友可以参考下
    2016-05-05
  • JavaOOP封装实例解读

    JavaOOP封装实例解读

    封装通过private限制属性访问,提供get/set方法控制数据读写,确保值合法,示例中Student类属性私有,Test类需调用set方法赋值并验证,get方法获取值,实现数据隐藏与安全操作
    2025-09-09

最新评论