SpringBoot 接口签名

核心原理

通过秘钥(不公开)对数据(请求数据)进行加密(加密算法公开),加密过程不可逆,所以只有拥有秘钥的双方才能对数据进行正确的加密,从而确保请求是由可信来源发出的,并且请求在传输过程中没有被篡改。

注:

  1. 接口签名其实还能用作防止请求参数不被篡改
  2. 一般appId和appSecret成对出现,因为可能秘钥有多个,此时就需要根据appId从数据库中查询appSecret

 

签名算法

  • 生成签名的方法通常将请求参数按照特定规则计算出一个签名值;
  • 验证签名的方法则是对接收到的请求参数进行同样的处理,并计算出一个签名值,然后与请求中携带的签名值进行比对;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
 
public class Signature {
    /**
     * 获取签名
     * @param secretKey 密钥
     * @param data  需要签名的数据
     * @return  签名
     */
    public static String signWithHmacSha1(String secretKey, String data) {
 
        try {
            SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signingKey);
            return Base64.getEncoder().encodeToString(mac.doFinal(data.getBytes("UTF-8")));
        } catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * 验证签名
     * @param secretKey 密钥
     * @param data  需要签名的数据
     * @param hmac  已经签名的数据
     * @return  true:签名一致
     */
    public static boolean verify(String secretKey, String data, String hmac) {
        String calculatedHmac = signWithHmacSha1(secretKey, data);
        return calculatedHmac.equals(hmac);
    }
}

 

请求拦截器

一般来说接口做了签名其实基本已经ok了,但为了更强的安全性,例如防止DDOS攻击、全局校验处理等,通常业界会将签名校验集成在拦截器中,与业务代码分离

注:如果项目中使用了认证授权框架,例如springsecurity,仍需要放行接口,不然请求会被拦截导致无法到底自定义的拦截器;(当然此处也可以改为自定义过滤器,加入springsecurity框架的过滤器链中)

自定义拦截器
 import com.xz.bd.business.utils.SignatureUtil;
import com.xz.bd.common.core.domain.R;
import lombok.extern.slf4j.Slf4j;
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;


@Slf4j
@Component
public class SignInterceptor implements HandlerInterceptor {
   // 此处需要使用注入bean的形式,才能获取到配置文件中的数据
    @Value("${secretKey}")
    private String secret;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * 对签名请求进行验证
         */
        String sign = request.getHeader("sign");
        String dateTime = request.getHeader("dateTime");

        if(System.currentTimeMillis()-Long.parseLong(dateTime) > 1000*30){
            log.error("数据大屏:请求签名已过期");
            return false;
        }
        if (sign == null || dateTime == null) {
            log.error("数据大屏:请求签名为空");
            return false;
        }
        if (SignatureUtil.verify(secret, dateTime, sign)) {
            return true;
        } else {
            log.error("数据大屏:请求签名验证失败");
            return false;
        }
    }
}
拦截器注册
 import com.xz.bd.business.interceptor.SignInterceptor;
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.WebMvcConfigurerAdapter;


@Configuration
public class SignInterceptorConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private SignInterceptor signInterceptor;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注入拦截器,并声明该拦截器对哪些请求生效
        registry.addInterceptor(signInterceptor).addPathPatterns("/dataScreen/**");
    }
}

 

 

参考链接

【1】https://blog.csdn.net/u011974797/article/details/138123261

 

posted @   先娶国王后取经  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
历史上的今天:
2020-12-24 Python数据分析(jupyter notebook上实现)
点击右上角即可分享
微信分享提示