对外接口签名生成方式

news2024/11/17 5:32:58

接口签名生成方式

前言

当某个系统对外部系统提供接口访问时,为提高接口请求安全性,往往会在接口访问时添加签名,当外部系统访问本系统签名验证成功时才能正常返回数据,一般接口提供方会与外部系统提前约定好,不同外部系统用 appKey 加以区分,并且不同 appKey 对应不同秘钥(secretKey)

签名生成方式

以下以 Get 请求为例:

  1. 第一步:在请求参数中添加 appKey 和时间戳 timestamp,将所有请求参数(除了 sign )按照字母排序
  2. 第二步:使用&将第一步参数拼接成如下形式( k1=v1&k2=v2&k3=v3 … ),并且将秘钥(secretKey)拼接在最后,最终字符串为 k1=v1&k2=v2&k3=v3secretKey
  3. 第三步:将第二步中的字符串使用MD5加密生成签名(sign )
  4. 第四步:将签名(sign )作为入参传入

以Java代码为例

定义两个项目,sign-provider 为接口提供方,sign-consumer 调用 sign-provider 提供的接口

sign-provider

接口提供方,提供接口为:
http://127.0.0.1:9091/provider/hello?query=2&offset=0&limit=10&appKey=A&sign={{sign}}&timestamp={{timestamp}}

其中 appKey 参数非固定传值,此处假设接口提供方与 sign-consumer 约定其 appKey 为 A ,secretKey(秘钥) 为 123456,sign 由接口调用方 sign-consumer 根据入参和秘钥拼接并通过MD5加密生成,具体规则看 签名生成方式

具体代码

package com.example.signprovider;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
import java.util.TreeMap;

@Slf4j
@RestController
@RequestMapping("/provider")
public class HelloController {

    private static final long EXPIRE_TIME = 5;

    /**
     * 不同系统对应不同 appKey 和 secretKey
     */
    private static final Map<String, String> APP_KEY_MAP = new HashMap<>();

    static {
        APP_KEY_MAP.put("A", "123456");
    }

    @GetMapping("/hello")
    public String hello(RequestBean requestBean) {
        //获取客户端 appKey
        String appKey = requestBean.getAppKey();
        Assert.isTrue(APP_KEY_MAP.containsKey(appKey), "无效appKey!");
        //客户端传入的签名
        String requestSign = requestBean.getSign();
        //检查有无传入签名
        Assert.hasText(requestSign, "无效签名!");
        long requestTime = requestBean.getTimestamp();
        //如果请求发起时间与当前时间超过expireTime,则接口请求过期
        Assert.isTrue(System.currentTimeMillis() / 1000 - requestTime <= EXPIRE_TIME, "请求过期!");
        //生成签名
        String sign = "";
        try {
            sign = getSign(requestBean, APP_KEY_MAP.get(appKey));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException("获取签名失败!");
        }
        //比对签名与传入签名是否一致
        Assert.isTrue(requestSign.equals(sign), "无效签名!");
        return "接口调用成功:" + requestBean;
    }

    private String getSign(RequestBean requestBean, String secretKey) throws IllegalAccessException {
        Map<String, Object> map = new TreeMap<>(String::compareTo);
        Field[] fields = requestBean.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (!"sign".equals(field.getName())) {
                field.setAccessible(true);
                map.put(field.getName(), field.get(requestBean));
            }
        }
        StringJoiner stringJoiner = new StringJoiner("&");
        map.forEach((k, v) -> stringJoiner.add(k + "=" + v));
        log.debug("stringJoiner:" + stringJoiner);
        String paramStr = stringJoiner + secretKey;
        //MD5加密
        return DigestUtils.md5DigestAsHex(paramStr.getBytes(StandardCharsets.UTF_8));
    }

}

如上:使用的MD5加密方法为 spring 提供的工具类 org.springframework.util.DigestUtils

sign-consumer

sign-provider 对外提供 API 为:
http://127.0.0.1:9091/provider/hello?query=2&offset=0&limit=10&appKey=A&sign={{sign}}&timestamp={{timestamp}}
其中 appKey、sign、timestamp 等参数均可由系统内部提供,所以 sign-consumer 对外提供接口为:
http://127.0.0.1:9092/consumer/hello?query=2&offset=0&limit=10

具体代码

package com.example.signconsumer;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.StringJoiner;
import java.util.TreeMap;

@Slf4j
@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    private static final String APP_KEY = "A";
    private static final String SECRET_KEY = "123456";

    private static final String URL = "http://127.0.0.1:9091/provider/hello";

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/hello")
    public String hello(RequestBean requestBean) {
        //使用 TreeMap 可对key排序
        Map<String, Object> params = new TreeMap<>();
        params.put("appKey", APP_KEY);
        params.put("timestamp", System.currentTimeMillis() / 1000);
        params.put("limit", requestBean.getLimit());
        params.put("offset", requestBean.getOffset());
        params.put("query", requestBean.getQuery());
        //生成签名
        String sign = getSign(params);
        log.debug("sign:{}", sign);
        params.put("sign", sign);
        StringJoiner stringJoiner = new StringJoiner("&");
        params.forEach((k, v) -> stringJoiner.add(k + "=" + v));
        ResponseEntity<String> result = restTemplate.getForEntity(URL + "?" + stringJoiner, String.class);
        return result.getBody();
    }

    private String getSign(Map<String, Object> params) {
        StringJoiner stringJoiner = new StringJoiner("&");
        params.forEach((k, v) -> stringJoiner.add(k + "=" + v));
        log.debug("stringJoiner:" + stringJoiner);
        String paramStr = stringJoiner.toString() + SECRET_KEY;
        return DigestUtils.md5DigestAsHex(paramStr.getBytes(StandardCharsets.UTF_8));
    }
}

测试

浏览器调用 sign-consumer 提供的接口:

代码路径

https://github.com/husgithub/sign-test

git 地址:

git@github.com:husgithub/sign-test.git

通过 PostMan 测试

通过调用 sign-provider 提供的接口可以测试 sign-consumer 提供的功能是否正确,PostMan 提供编写脚本的能力,在 JS 脚本中我们可以生成 timestamp 、sign 参数的值

打开 PostMan 后定位到 Pre-request Script 栏,可在此写 JS 脚本:

脚本如下:

console.log("start......");
var timestamp = Math.floor(new Date().getTime()/1000);
pm.globals.set("timestamp", timestamp);
console.log("----");
console.log(request.url);
//console.log(pm.request.url.query.get("timestamp"));
var paramStr = request.url.split("?")[1];
console.log("url参数字符串为:"+paramStr);
console.log("分割字符串参数......");
var map = new Map();
var paramArr = paramStr.split("&");
for(var i=0;i<paramArr.length;i++){
    var p = paramArr[i].split("=");
    if("sign"!=(p[0])){
        if("timestamp"==p[0]){
            map.set(p[0],timestamp);
        }else{
            map.set(p[0],p[1]);
        }
    }
}
console.log(paramArr);
console.log(map.size);

//对map排序
var arrayObj = Array.from(map);
arrayObj.sort(function (a, b) {
    return a[0].localeCompare(b[0])
});
var sortParamStr = "";
for (var [key, value] of arrayObj) {
    console.log(key + ' = ' + value);
    sortParamStr += "&"+key+"="+value;
}
console.log(sortParamStr.substring(1));
//添加秘钥
var signStr = sortParamStr.substring(1)+"123456";
//生成签名
var sign = CryptoJS.MD5(signStr).toString();
console.log(sign);
pm.globals.set("sign", sign);

如下图:
通过 {{sign}} 的方式可以定义变量,之后可以通过 js 脚本对变量进行赋值

通过 request.url 可以获取请求 URL:

request.url

pm.globals.set(“sign”, sign); 表示对 {{sign}} 括号内的参数赋值:

pm.globals.set("sign", sign);

测试

通过 View -> Show Postman Console 可以打开 PostMan Console 控制台

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/814550.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

六天入门PyTorch深度学习(1/6)

六天带你入门PyTorch深度学习(1/6) 之PyTorch初认识 Pytorch深度学习快速入门简易教程&#xff0c;适合所有新手学习打好框架基础 跟着我的节奏一步一步学&#xff0c;一周即可掌握 跟着我的节奏一步一步学&#xff0c;一周即可掌握 import torch #导入torch库&#xff0c…

django4.2 day1Django快速入门

1、创建虚拟环境 打开cmd安装virtualenv pip install virtualenvwrapper-winworkon 查看虚拟环境mkvirtualenv 创建新的虚拟环境删除虚拟环境 rmvirtualenv 进入虚拟环境 workon env 2、创建django虚拟环境并安装django 创建虚拟环境mkvirtualenv django4env进入虚拟环境安…

Mycat-Balance使用指南

MyCAT Balance是一个Java NIO的高性能负载均衡器&#xff0c;可以替代普通的硬件的交换机或其LVS类似的复杂机制&#xff0c;实现MyCAT集群的负载均衡。 MyCAT Balance的配置文件在conf目录下&#xff0c;frontend-conf.为前端配置&#xff0c;包括绑定的端口等&#xff0c;js…

牛客网Verilog刷题——VL45

牛客网Verilog刷题——VL45 题目解析答案 题目 请根据题目中给出的双口RAM代码和接口描述&#xff0c;实现异步FIFO&#xff0c;要求FIFO位宽和深度参数化可配置。电路的接口如下图所示。 双口RAM端口说明&#xff1a; 异步FIFO端口说明&#xff1a; 双口RAM代码如下&#xff0…

第七章 HL7 架构和可用工具 - 使用 HL7 消息查看器页面

文章目录 第七章 HL7 架构和可用工具 - 使用 HL7 消息查看器页面使用 HL7 消息查看器页面选择选项解析消息 第七章 HL7 架构和可用工具 - 使用 HL7 消息查看器页面 使用 HL7 消息查看器页面 为 HL7 提供了消息查看器页面。可以使用此页面显示、转换和导出 HL7 消息&#xff0…

【力扣每日一题】2023.7.31 重排链表

目录 题目&#xff1a; 示例: 分析: 代码: 题目&#xff1a; 示例: 分析: 给我们一个链表&#xff0c;让我们按照题目要求原地修改重排链表。 那么具体怎么个重排法呢&#xff0c;题目给出了一串式子&#xff0c;其实就是把链表分为前后两段&#xff0c;然后在前半段的节…

【UDS诊断】:学习记录

学习记录 诊断分层诊断命令诊断理解UDS的寻址模式UDS的服务类型 参考文件 诊断分层 &#xff08;上述图片来源于&#xff1a;ISO 14229-1-2013&#xff09; UDS包含了ISO 14229下属的7个子协议 诊断命令 UDS的请求命令有4种构成方式&#xff1a; SIDSIDSF&#xff08;Sub-fu…

开源项目audioFlux: 针对音频领域的深度学习工具库

audioFlux是一个Python和C实现的库&#xff0c;提供音频领域系统、全面、多维度的特征提取与组合&#xff0c;结合各种深度学习网络模型&#xff0c;进行音频领域的业务研发&#xff0c;下面从时频变换、频谱重排、倒谱系数、解卷积、谱特征、音乐信息检索六个方面简单阐述其相…

当 Spark 任务出现数据倾斜的问题时该如何处理呢?

前言 最近一位数仓同学问我,当 Spark 任务出现数据倾斜的问题时该如何处理呢?那么今天就来讲一下遇到了数据倾斜问题时处理的方式方法。 1)数据倾斜的定义 Spark 中的数据倾斜问题主要指 shuffle 过程中出现的数据倾斜问题,是由于不同的 key 对应的数据量不同导致的不同t…

【计算机视觉 | 目标检测 | 图像分割】arxiv 计算机视觉关于目标检测和图像分割的学术速递(7 月 27 日论文合集)

文章目录 一、检测相关(6篇)1.1 Memory-Efficient Graph Convolutional Networks for Object Classification and Detection with Event Cameras1.2 PNT-Edge: Towards Robust Edge Detection with Noisy Labels by Learning Pixel-level Noise Transitions1.3 Controllable Gu…

C/C++程序的翻译与执行

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;> c语言学习 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是…

(4)将固件加载到已有ArduPilot固件的主板上

文章目录 前言 4.1 将自动驾驶仪连接到电脑 4.2 选择COM端口 4.3 安装固件 4.4 使用测试版和开发版 前言 这些说明将告诉你如何将最新的固件下载到已经安装了 ArduPilot 固件的自动驾驶仪硬件上。这个过程将使用 Mission Planner 地面控制站。请参阅将固件加载到没有 Ard…

AttributeError: ‘DataFrame‘ object has no attribute ‘iteritems‘解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

Vue3+Vue2表单点击加号实现多选效果(以逗号分割传递后端参数,及数据回显处理)

示图如下: 一、Vue3举例 嵌套在form表单,利用elment-ui行布局,将输入表单填充至内容区域<el-row type="flex" v-for="(domain, index) in dataForm.dataValue" :key&#

力扣 316. 去除重复字母 C++

目录 题目要求 思路 力扣提交AC代码 C代码 题目要求 删去一些重复元素保持原有顺序使字典序最小 思路 遍历一个字符串 并维护一个栈如果当前字符已经再栈内就直接跳过&#xff08;具体见程序&#xff09;当 栈顶元素 > 当前元素&#xff0c;那么将栈顶出栈&#xff0…

【利诱和强制分享下载】规则修改指引

代码审核环节&#xff0c;将会对小程序运营的内容进行核实是否存在阻断功能&#xff0c;损害用户体验。 常见利诱诱导类型&#xff1a; 1、利诱下载APP 小程序内出现不断弹窗、频繁提示诱导用户下载APP&#xff0c;强制用户必须下载APP才能体验完整功能服务。 示例&#xf…

靶机精讲之NYX

主机发现 端口扫描 服务扫描 -sV漏洞脚本扫描 UDP扫描 那些开发那些关闭 脚本扫描 或许有价值.php web渗透 看源码 目录爆破 没有扫到有价值的信息 继续web渗透&#xff0c;访问admin目录 继续目录扫描 sudo gobuster dir -u http://192.168.10.206 -x txt,sql,php,jsp -w…

Learning-Rate-Free Learning by D-Adaptation

考虑无约束凸Lipshcitz优化问题: min ⁡ x ∈ R n f ( x ) . \min_{x\in\mathbb{R}^{n}}f(x). x∈Rnmin​f(x). 最常用的优化方法是子梯度下降(Subgradient descent): x k 1 x k − γ k g k , x_{k1}x_{k}-\gamma_{k}g_{k}, xk1​xk​−γk​gk​, 其中 g k ∈ ∂ f ( x …

芯旺微冲刺IPO,车规级MCU竞争白热化下的“隐忧”凸显

在汽车智能化和电动化发展带来的巨大蓝海市场下&#xff0c;产业链企业迎来了一波IPO小高潮。 日前&#xff0c;上海芯旺微电子技术股份有限公司&#xff08;以下简称“芯旺微”&#xff09;在科创板的上市申请已经被上交所受理&#xff0c;拟募资17亿元&#xff0c;用于投建车…