SpringBoot前后端分离框架中,在请求头加入签名

SpringBoot前后端分离框架中,要在请求头加入签名(Sign) ,核心是:前端在请求拦截器统一加签,后端通过拦截器统一验签

一、前端实现(Vue/axios):在请求头加签名

1. 安装依赖
bash 复制代码
npm install js-md5 --save
2. 配置签名密钥(建议单独文件)

创建 @/config/signature.js

javascript 复制代码
// 签名密钥(前后端必须一致,建议从环境变量读取)
export const SIGN_SECRET = 'your-strong-secret-key-2026';
// 参数分隔符
export const SIGN_SEPARATOR = '|';
3. 修改请求拦截器(@/utils/request.js
javascript 复制代码
import axios from 'axios';
import Md5 from 'js-md5';
import { SIGN_SECRET, SIGN_SEPARATOR } from '@/config/signature';

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 10000
});

// 请求拦截器:加入签名
service.interceptors.request.use(config => {
  // 1. 生成时间戳(毫秒)
  const timestamp = Date.now().toString();
  
  // 2. 构建待签字符串(建议:密钥 + 时间戳 + 请求方法 + URL)
  const method = config.method.toUpperCase();
  const url = config.url;
  const stringToSign = [SIGN_SECRET, timestamp, method, url].join(SIGN_SEPARATOR);
  
  // 3. 生成MD5签名(转大写)
  const sign = Md5(stringToSign).toUpperCase();
  
  // 4. 放入请求头
  config.headers['timestamp'] = timestamp;
  config.headers['sign'] = sign;
  
  return config;
}, error => {
  return Promise.reject(error);
});

export default service;

二、后端实现(Java/Spring Boot):拦截器验签

1. 自定义签名拦截器
java 复制代码
package com.ruoyi.framework.interceptor;

import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;

@Component
public class SignatureInterceptor implements HandlerInterceptor {

    // 从配置文件读取密钥
    @Value("${signature.secret}")
    private String SIGN_SECRET;
    
    private static final String SIGN_SEPARATOR = "|";
    // 签名有效时间(毫秒,例如5分钟)
    private static final long EXPIRE_TIME = 5 * 60 * 1000;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 1. 从请求头获取参数
        String timestamp = request.getHeader("timestamp");
        String clientSign = request.getHeader("sign");
        String method = request.getMethod();
        String url = request.getRequestURI();

        // 2. 非空校验
        if (StrUtil.hasBlank(timestamp, clientSign)) {
            throw new ServiceException("缺少签名参数");
        }

        // 3. 时间戳过期校验
        long currentTime = System.currentTimeMillis();
        long reqTime = Long.parseLong(timestamp);
        if (Math.abs(currentTime - reqTime) > EXPIRE_TIME) {
            throw new ServiceException("请求已过期");
        }

        // 4. 后端重新生成签名(与前端规则一致)
        String stringToSign = String.join(SIGN_SEPARATOR,
                SIGN_SECRET, timestamp, method, url);
        String serverSign = StrUtil.upper(StrUtil.md5(stringToSign));

        // 5. 比对签名
        if (!serverSign.equals(clientSign)) {
            throw new ServiceException("签名验证失败");
        }

        return true;
    }
}
2. 注册拦截器(WebConfig.java
java 复制代码
package com.ruoyi.framework.config;

import com.ruoyi.framework.interceptor.SignatureInterceptor;
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 SignatureInterceptor signatureInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(signatureInterceptor)
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns(
                        "/login", "/logout", "/captchaImage" // 放行登录等接口
                )
                .order(1); // 优先于登录拦截
    }
}
3. 配置文件(application.yml
yaml 复制代码
# 签名配置
signature:
  secret: your-strong-secret-key-2026

三、安全增强(可选)

  • 加入参数签名:将请求参数(query/body)按key排序后拼接,防止参数篡改。
  • 使用HMAC-SHA256:比MD5更安全。
  • 动态密钥:按用户/应用分配不同密钥。
  • 全局异常处理:统一返回签名错误JSON。

四、常见问题

  • 跨域问题 :在 CorsConfig 中暴露请求头:

    java 复制代码
    config.setExposedHeaders(Arrays.asList("timestamp", "sign"));
  • 放行接口漏配:登录、验证码、WebSocket等必须排除。

相关推荐
吴声子夜歌5 分钟前
Java面试——Spring Cloud原理及应用(二)
java·spring cloud·面试
SomeB1oody25 分钟前
【RustyML入门】6.3. 并行归约
开发语言·后端·机器学习·rust·教程
东小西1 小时前
【SAA实战】第 1 篇:ReactAgent 入门——先撸个"会调工具的助手"跑起来
java·人工智能·spring
Profile排查笔记1 小时前
指纹浏览器哪个好?从 Profile、代理、权限和自动化能力判断是否适合
前端·人工智能·后端·自动化
用户938515635072 小时前
用 AI 结对编程从 0 搭一个"单词后台管理系统":Next.js + Supabase + Drizzle + shadcn/ui 全记录
后端·postgresql·next.js
mqiqe2 小时前
AgentScope Java 2.0 协议集成全景解析:A2A、AG-UI、Agent Protocol 三大开放协议实战指南
java·开发语言·ui
脉动数据行情2 小时前
Java 实现台股 TWSE/TPEx 行情采集(个股 + K 线)
java·开发语言·twse·tpex·台股
爱学习的小邓同学2 小时前
Golang --- (1)第一个Golang程序
开发语言·后端·golang
小灰灰搞电子3 小时前
Rust+Slint 实现的“DNA双螺旋”加载动画源码分享
后端·rust·slint·加载动画
面向Google编程3 小时前
向量库不再囤数据:Milvus 3.0 零拷贝直读数据湖
后端