当前位置: 首页 > news >正文

设计接口时,为其添加签名鉴权---详细教程

 一、何为签名

        我们知道无论是restful api还是传统接口、亦或是其他形式接口的调用,接口签名都是非常重要的安全机制,它可以确保请求的发起者是经过认证和授权的客户端,同时也可以防止接口被攻击,请求参数被篡改等等。

        用大白话来解释就是,如果你写的接口没有签名验证,那么不管是谁都可以调用,只要路径、参数是对的就可以调用,这样就会非常不安全,因此我们会对接口添加签名验证。

        首先,我们要了解原理,举个例子来讲,假设有一场线上的会议,而会议室呢并不是谁都可以进去的,光有会议室号是不行的还需要密码,如果更严格一些,可能还需要填写公共的信息内容等,那这些密码信息等,都是实现约定好的,提供给相关人就可以。

       同理,签名也是双方约定好的,客户端(调用接口的用户)需要带着事先约定好的签名去调用,然后服务端(被调用)需要对这个签名进行验证,如果是他约定好的,那么就放行,让其直接调用即可。

二、签名设计

    那么签名该如何设计呢?那就要说道签名的规则了。

签名一般会包括以下几部分:

  1.  appid 和 app\secret
  2.  timestamp 时间戳
  3.  version 版本号
  4.  signature 所有数据的签名信息。

 接下来依次介绍他们的作用:

1) appid & appsecret

  appid :应用的标识

  appsecret:私匙(相当于密码)

    通常我们会线下分配appidappsecret,也就是提前声明好,针对不同的调用方分配不同的appid和appsecret。一般来说appid 和appsecret是一一对应的,用于对接口数据进行加密、解密。

2) timestamp(时间戳)用时间戳可以防止暴力请求

    sign机制可以防止参数被篡改,但无法防ddos攻击(第三方使用正确的参数,不停的请求服务器,使之无法正常提供服务)。因此,还需要引入时间戳机制。

    而服务端则需要根据当前时间和sign值的时间戳进行比较,差值超过一段时间则不予通过客户端的请求,直接给客户端响应某些错误提示等。

   若要求不高,则客户端和服务端可以仅仅使用精确到秒或分钟的时间戳,据此形成sign值来校验有效性。这样可以使一秒或一分钟内的请求是有效的。

   若要求较高,则还需要约定一个解密算法,使服务端可以从sign值中解析出发起请求的时间戳。

3) 版本号version

    防止重复提交,至少为10位。针对查询接口,版本号只用于日志落地,便于后期日志核查。 针对办理类接口需校验流水号在有效期内的唯一性,以避免重复请求。

4)signature

    signature 的规则可以随便定义,按照自己的想法即可,我们以 md5(sort(body)+appSecret)举例,即 先对请求体的字段按照字母a-z顺序进行排序,然后在其尾部拼接appSecret,再经过md5计算出签名,然后用户在请求中添加签名即可。

   正常会将appid、timestamp、nonce、signature这四个字段放入请求头中,但我们这里暂时定义规则为只要将body中的key按照大小顺序排好序以后,在拼接上appsecret即可。

三、代码演示

1、传统的http请求
package com.demo.test;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.jfinal.aop.Clear;
import com.jfinal.core.Controller;
import com.jfinal.kit.Prop;
import com.jfinal.kit.PropKit;
import oracle.jdbc.proxy.annotation.Post;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;import javax.servlet.ServletInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;@Clear
public class DemoController extends Controller {private static final long EXPIRE_TIME = 10 * 60 * 1000; // 10分钟的毫秒数public void save() throws IOException {// 检查请求方法是否为 POSTif (!"POST".equalsIgnoreCase(getRequest().getMethod())) {System.out.println("Request method"+ getRequest().getMethod());renderText("Only POST requests are allowed for this method.");return;}// 获取请求头信息String headerValue = getHeader("sign");System.out.println("Header Value: " + headerValue);// 获取请求参数String paramValue = getPara("paramName");System.out.println("Request Parameter Value: " + paramValue);// 获取请求 URLString requestUrl = getRequest().getRequestURL().toString();System.out.println("Request URL: " + requestUrl);// 获取请求体信息String requestBody = getRawRequestBody(getRequest().getInputStream());System.out.println("Request Body: " + requestBody);JSONObject pass = validateSign(headerValue,requestBody);if(pass.getString("success").equals(false)){logger.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}"+requestUrl+ JSON.toJSONString(paramValue));
//            String url =request.getRequestURI()+"?"+ getIpAddress(request)+ JSON.toJSONString(request.getParameterMap());
//            CacheKit.put("eam", "url", url);renderJson(pass);}else{//成功逻辑renderJson(pass);}
//        renderText("POST request processed");}private JSONObject validateSign(String requestSign,String requestBody) throws IOException {JSONObject reponseJson = new JSONObject();String msg;boolean flag;try {if (StringUtils.isEmpty(requestSign)) {logger.info("签名为空, signature=" + requestSign);msg = "签名为空";reponseJson.put("msg", msg);reponseJson.put("flag", false);return reponseJson;}//时间戳JSONObject jsonObject = JSONObject.parseObject(requestBody);Long now = System.currentTimeMillis();Long requestTimestamp = Long.parseLong( jsonObject.getString("timestamp"));if ((now - requestTimestamp) > EXPIRE_TIME) {logger.info("请求时间超过规定范围时间10分钟, signature=" + requestSign);msg = "请求时间超过规定范围时间10分钟";reponseJson.put("msg", msg);reponseJson.put("flag", false);return reponseJson;}//排序 requestBodyString body = sortJsonString(requestBody);//获取密钥,这里密钥为了方便,写在了配置文件中,可随意定义一些复杂内容如jgjgro4h3h5b5b5b9nnkx0fkeProp p = PropKit.use("config.properties");String secret = p.get("emsSecret");String ls = body + secret;String sign = DigestUtils.md5Hex(ls.getBytes());//混合密钥md5,加密后的sign,用它来和请求传过来的sign对比,如果一致则通过if(StringUtils.equals(sign, requestSign)){logger.info("请求时间超过规定范围时间10分钟, signature=" + requestSign);msg = "请求成功!";reponseJson.put("msg", msg);reponseJson.put("flag", true);return reponseJson;}else {msg = "请求失败!sign不匹配";reponseJson.put("msg", msg);reponseJson.put("flag", false);return reponseJson;}}catch (Exception e){logger.error("Error in execute EmaController. The wrong message is:"+ e.getMessage());}msg = "请求失败";reponseJson.put("msg", msg);reponseJson.put("flag", false);return reponseJson;}private static String sortJsonString(String requestBody) throws JsonProcessingException {// 将 JSON 字符串转换为 JsonNodeObjectMapper objectMapper = new ObjectMapper();JsonNode jsonNode = objectMapper.readTree(requestBody);// 使用 TreeMap 对属性进行排序TreeMap<String, JsonNode> sortedProperties = new TreeMap<>();Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();while (fields.hasNext()) {Map.Entry<String, JsonNode> entry = fields.next();sortedProperties.put(entry.getKey(), entry.getValue());}// 创建新的 JsonNode,按照字母a-z的顺序排列JsonNode sortedJsonNode = objectMapper.valueToTree(sortedProperties);// 将排序后的 JsonNode 转换回 JSON 字符串String sortedJsonString = objectMapper.writeValueAsString(sortedJsonNode);// 输出排序后的 JSON 字符串return sortedJsonString;}// 读取请求体的方法private String getRawRequestBody(ServletInputStream inputStream) {StringBuilder stringBuilder = new StringBuilder();BufferedReader bufferedReader = null;try {bufferedReader = new BufferedReader(new InputStreamReader(inputStream));char[] charBuffer = new char[128];int bytesRead;while ((bytesRead = bufferedReader.read(charBuffer)) != -1) {stringBuilder.append(charBuffer, 0, bytesRead);}} catch (IOException e) {e.printStackTrace();} finally {if (bufferedReader != null) {try {bufferedReader.close();} catch (IOException e) {e.printStackTrace();}}}return stringBuilder.toString();}
}
2、Restful风格

这里由于无法直接获取请求体,因此封装了个方法getRawRequestBody(), 如果是采用restful风格开发,可以直接通过@ReqequstBody Map<String String>params获取请求体,如:

@RestController
public class UserController {@PostMapping("/test")public void createUser(@RequestBody Map<String String> body) {// 在这里处理System.out.pring(body);}
}

获取请求参数如:

@RequestMapping(value = "/test", method = RequestMethod.POST)
@ResponseBody
public StringTestUrl(@RequestParam("username")String username,         @RequestParam("pwd")String pwd)  {String txt = username + pwd;return txt;
}

 暂时先写到这里。。。未完

相关文章:

设计接口时,为其添加签名鉴权---详细教程

一、何为签名 我们知道无论是restful api还是传统接口、亦或是其他形式接口的调用&#xff0c;接口签名都是非常重要的安全机制&#xff0c;它可以确保请求的发起者是经过认证和授权的客户端&#xff0c;同时也可以防止接口被攻击&#xff0c;请求参数被篡改等等。 用大白话来解…...

5G+物联网:连接万物,重塑智慧社区,开启未来生活新纪元,助力智慧社区的革新与发展

一、5G与物联网&#xff1a;技术概述与基础 随着科技的飞速发展&#xff0c;第五代移动通信技术&#xff08;5G&#xff09;和物联网&#xff08;IoT&#xff09;已经成为当今社会的热门话题。这两项技术作为现代信息社会的核心基础设施&#xff0c;正深刻地改变着人们的生活和…...

[反转链表] [合并两个有序链表][分割链表]

这里写目录标题 反转链表合并两个有序链表分割链表 反转链表 1、题目&#xff1a; 2.思路  思路1&#xff1a;建立一个newHead,取一个节点进行头插。具体做法如下&#xff01; 建立一个newHead(新头)&#xff0c;由于一个节点里面存的是下一个节点的地址&#xff0c;如果取…...

中文数据让LLM变笨?

我这里先贴一下论文的原链接&#xff1a; https://arxiv.org/abs/2401.10286 然后贴一下我翻译标注的下载链接&#xff1a;https://gitee.com/chatpaper/arXiv_top_chinese/blob/master/0801_top/%E4%B8%AD%E6%96%87%E4%BC%9A%E8%AE%A9LLM%E5%8F%98%E7%AC%A8%EF%BC%9F.pdf 先…...

【代码随想录】刷题笔记Day54

前言 差单调栈就结束代码随想录一刷啦&#xff0c;回家二刷打算改用python补充进博客&#xff0c;小涛加油&#xff01;&#xff01;&#xff01; 647. 回文子串 - 力扣&#xff08;LeetCode&#xff09; 双指针法 中心点外扩&#xff0c;注意中心点可能有一个元素可能有两个…...

二.Winform使用Webview2在Demo1中实现地址简单校验

Winform使用Webview2在Demo1中实现地址简单校验 往期目录回顾添加对于的简单url验证提示通过上节和本节涉及到的函数有 往期目录 往期相关文章目录 专栏目录 回顾 通过一.Winform使用Webview2(Edge浏览器核心) 创建demo(Demo1)实现回车导航到指定地址 我们已经知道了解决资源…...

从0开始学习C++ 第二十课:模板与泛型编程

第二十课&#xff1a;模板与泛型编程 学习目标&#xff1a; 掌握模板的基本语法和概念。学会使用函数模板来创建可重用的函数。学习如何定义类模板以实现数据结构的泛型。理解模板在C中提供的灵活性和强大功能。 学习内容&#xff1a; 模板的概念&#xff1a; 模板是C中支持…...

pcl之滤波器(一)

pcl滤波器 pcl一共是有十二个主要模块&#xff0c;详细了解可以查看官网。https://pcl.readthedocs.io/projects/tutorials/en/latest/#basic-usage 今天学习一下pcl的滤波器模块。 滤波器模块&#xff0c;官网一共是提供了6个例程&#xff0c;今天先来看第一第二个。 直通…...

java项目性能优化(MyBatis中开启查询缓存及flushCache与useCache的使用)

在java项目中&#xff0c;如果需要大量的DB查询&#xff0c;导致缓存过多&#xff0c;项目运行缓慢&#xff0c;可以设置在select查询时&#xff0c;添加二级缓存的清空。 如果没有去配置flushCache、useCache&#xff0c;那么默认是启用缓存的。 1&#xff0c;flushCache默认…...

Unity3D控制人物移动的多种方法

系列文章目录 unity知识点 文章目录 系列文章目录前言一、人物移动之键盘移动1-1、代码如下1-2、效果 二、人物移动之跟随鼠标点击移动2-1、代码如下2-2、效果 三、人物移动之刚体移动3-1、代码如下3-2、效果 四、人物移动之第一人称控制器移动4-1、代码如下4-2、效果 五、And…...

无人机打击激光器

激光器的应用非常广泛&#xff0c;涵盖了多个领域。以下是一些主要的激光器应用&#xff1a; 医疗领域&#xff1a;激光器在医疗行业中有着重要应用&#xff0c;比如用于激光手术&#xff08;如眼科手术&#xff09;、皮肤治疗、牙科治疗、肿瘤治疗等。 工业制造&#xff1a;在…...

Lingo数学建模基础

1.基本运算符 1.1算数运算符 1.2逻辑运算 #not# 否定操作数的逻辑值&#xff0c;一元运算符 #eq# 若两运算数相等&#xff0c;则为true,否则为false #ne# 若两运算数不相等&#xff0c;则为true,否则为false #gt# 若左边运算数严格大于右边&#xff0c;则为true,否则为…...

finalshell连接linux的kali系统

kali的ssh服务似乎是默认关闭的&#xff0c;笔者在玩CentOS系统时可以直接用finalshell完成连接&#xff0c;但kali不行&#xff0c;需要先手动开启ssh服务。 开启kali的ssh服务 输入【ssh start】命令开启ssh服务&#xff0c;可以用【ssh status】命令查看ssh状态&#xff0c…...

2、Line Charts折线图

可视化时间趋势 现在你已经熟悉了编码环境,是时候学习如何制作自己的图表了! 在本教程中,您将学习足够的Python来创建专业外观的折线图。然后,在接下来的练习中,您将使用您的最新技能处理真实世界的数据集。 本课程数据集夸克网盘下载链接:https://pan.quark.cn/s/a235ac…...

shell脚本获得所有数据库备份(整库备份,表级备份)

数据库备份到天翼云对象存储OBS https://blog.csdn.net/qq_34631220/article/details/135755894 1、获得所有数据库 #!/bin/sh HOSTNAME"ip" #数据库信息 PORT"3306" USERNAME"root" PASSWORD"" DBNAME"yusuan" #数据库…...

REVIT二次开发万能刷

将这两个参数赋予其他参数 步骤2 将来做个可以调控的版本 using System; using System.Collections.Generic; using System.Lin...

JSON简单了解

文章目录 1、JSON介绍2、ES6模版字符串3、JS对象转化为JSON字符串3.1、手动JS对象转化为JSON字符串3.2、自动JS对象转化为JSON字符串 4、JS对象和java互相转换 1、JSON介绍 JSON 概念&#xff1a;JavaScript Object Notation。JavaScript 对象表示法&#xff0c;简单理解JSON是…...

HarmonyOS鸿蒙应用开发( 四、重磅组件List列表组件使用详解)

List列表组件&#xff0c;是一个非常常用的组件。可以说在一个应用中&#xff0c;它的身影无处不在。它包含一系列相同宽度的列表项&#xff0c;适合连续、多行呈现同类数据&#xff0c;如商品列表、图片列表和和文本列表等。ArkUI 框架采用 List 容器组件创建列表&#xff08;…...

redis优化系列(六)

本期分享redis内存过期策略&#xff1a;过期key的处理 Redis之所以性能强&#xff0c;最主要的原因就是基于内存存储。然而单节点的Redis其内存大小不宜过大&#xff0c;会影响持久化或主从同步性能。 可以通过修改配置文件来设置Redis的最大内存&#xff1a; maxmemory 1gb …...

【 Qt 快速上手】-②- Qt 环境搭建

文章目录 1. Qt 开发工具概述1.1 Qt Creator 介绍1.2 Visual Studio 介绍1.3 Eclipse 介绍 2. Qt SDK 的下载与安装2.1 Qt SDK 的下载2.2 Qt SDK 的安装2.3 验证 Qt SDK 安装是否成功2.4 Qt 环境变量配置 1. Qt 开发工具概述 Qt 开发环境需要安装三个部分&#xff1a; C编译器…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...