Java实现API sign签名校验的方法详解

目录
  • 1. 前言
  • 2. 签名生成策略
  • 3. API 签名算法 Java 实现
  • 4. 测试一下

1. 前言

目的:为防止中间人攻击。

场景:

  • 项目内部前后端调用,这种场景只需要做普通参数的签名校验和过期请求校验,目的是为了防止攻击者劫持请求 url 后非法请求接口。
  • 开放平台向第三方应用提供能力,这种场景除了普通参数校验和请求过期校验外,还要考虑 3d 应用的授权机制,不被授权的应用就算传入了合法的参数也不能被允许请求成功。

2. 签名生成策略

接下来详述场景 2,其实场景 1 也包含在场景 2 内部。

1.举例请求 url:

http://api.abc.com/a-service/orders?orderType=1001&requestFrom=IOS&pageNum=2&pageSize=10

请求参数为:

参数名 位置 备注 举例
X-Access-Key header 客户端授权码,服务端提供,和 accessSecret 配对(场景 1 无此参数) app1
X-Access-Token header 当前登录用户 token d7b5808c3f443eb5a496225468c7e4a5
X-UTCTime header 当前发送请求时的时间 2022-02-16T09:12:43.083Z
X-Random header 请求随机数 341be97d9aff90c9978347f66f945b77
orderType query 订单类型 1001
requestFrom query 订单来源 IOS
pageNum query 分页参数 10
pageSize query 分页参数 2

2.设原始参数为 stringA,stringA 中添加 X-Access-Key、X-UTCTime、X-Random 固定参数,将 stringA 内非空参数值和 header 的参数按照参数名 ASCII 码从小到大排序(字典序),使用 URL 键值对的格式(即 key1=value1&key2=value2…)拼接成字符串 stringB。

注意如下规则:

  • 参数名 ASCII 码从小到大排序(字典序);
  • 如果参数的值为空不参与签名;
  • 参数名区分大小写;
  • 验证调用返回或主动通知签名时,传送的 sign 参数不参与签名,将生成的签名与该 sign 值作校验。
// 最终拼接为stringB:
orderType=1001X-UTCTime=2022-02-16T09:12:43.083ZpageSize=10X-Access-Key=app1X-Access-Token=d7b5808c3f443eb5a496225468c7e4a5pageNum=2requestFrom=IOSX-Random=341be97d9aff90c9978347f66f945b77

3.在 stringB 最后拼接上 accessSecret (密钥) 得到 stringC 字符串,并对 stringC 进行 MD5 运算,得到 sign 值。

// 最后拼上accessSecret得到stringC:
orderType=1001X-UTCTime=2022-02-16T09:12:43.083ZpageSize=10X-Access-Key=app1X-Access-Token=d7b5808c3f443eb5a496225468c7e4a5pageNum=2requestFrom=IOSX-Random=341be97d9aff90c9978347f66f945b77&accessSecret=192006250b4c09247ec02edce69f6a2d
// md5加密得到最终签名结果sign:
sign=e1a4907ef03adee3fa8d395552814f4e

4.将原始的请求 url 拼接上 sign 形成最终的请求 url。

http://api.abc.com/a-service/orders?orderType=1001&requestFrom=IOS&pageNum=2&pageSize=10&sign=0f5a3cc534961d129a25d52d7ed8d003

5.最终请求 url 如下:

http://api.abc.com/a-service/orders?orderType=1001&requestFrom=IOS&pageNum=2&pageSize=10&sign=0f5a3cc534961d129a25d52d7ed8d003

参数名 位置 备注 举例
X-Access-Key header 客户端授权码,服务端提供,和 accessSecret 配对(场景 1 无此参数) app1
X-Access-Token header 当前登录用户 token d7b5808c3f443eb5a496225468c7e4a5
X-UTCTime header 当前发送请求时的时间 2022-02-16T09:12:43.083Z
X-Random header 请求随机数 341be97d9aff90c9978347f66f945b77
orderType query 订单类型 1001
requestFrom query 订单来源 IOS
pageNum query 分页参数 10
pageSize query 分页参数 2

6.服务端 gateway 同样做 sign 签名加密和校验,如果校验不通过则说明请求非法,直接拒绝,通过则下发到业务服务进行正常请求处理。

3. API 签名算法 Java 实现

public class SignUtil {

    /**
     * 生成签名
     *
     * @param accessSecret accessSecret
     * @param url          url
     * @param headers      headers
     * @param body         post的body体
     * @param <T>          body体泛型
     * @return sign
     */
    public static <T> String sign(String accessSecret, String url, Map<String, Object> headers, T body) throws IllegalAccessException {
        Map<String, Object> signMap = new HashMap<>();
        if (headers != null) {
            signMap.putAll(headers);
        }
        Map<String, Object> paramMap = getUrlParams(url);
        if (paramMap != null) {
            signMap.putAll(paramMap);
        }
        Map<String, Object> bodyMap = getBodyParams(body);
        if (bodyMap != null) {
            signMap.putAll(bodyMap);
        }

        StringBuffer sb = new StringBuffer();
        signMap.forEach((k, v) -> {
            sb.append(k).append("=").append(v).append("&");
        });
        sb.append("accessSecret=").append(accessSecret);
        return stringToMD5(sb.toString());
    }

    private static Map<String, Object> getUrlParams(String url) {
        if (StringUtils.isBlank(url) || !url.contains("?")) {
            return null;
        }
        Map<String, Object> paramMap = new HashMap<>();
        String params = url.split("\\?")[1];
        for (String param : params.split("&")) {
            String[] p = param.split("=");
            paramMap.put(p[0], p[1]);
        }
        return paramMap;
    }

    private static <T> Map<String, Object> getBodyParams(T body) throws IllegalAccessException {
        if (body == null) {
            return null;
        }
        Map<String, Object> bodyMap = new HashMap<>();
        for (Field field : body.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            bodyMap.put(field.getName(), field.get(body));
        }
        return bodyMap;
    }

    private static String stringToMD5(String plainText) {
        byte[] secretBytes = null;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("没有这个md5算法!");
        }

        return new BigInteger(1, secretBytes).toString(16);
    }
}

4. 测试一下

public class App {
    public static void main(String[] args) throws IllegalAccessException {
        String url = "http://api.abc.com/a-service/orders?orderType=1001&requestFrom=IOS&pageNum=2&pageSize=10";
        Map<String, Object> headerMap = new HashMap<>();
        headerMap.put("X-Access-Key", "app1");
        headerMap.put("X-Access-Token", "d7b5808c3f443eb5a496225468c7e4a5");
        headerMap.put("X-UTCTime", generateDate());
        headerMap.put("X-Random", "341be97d9aff90c9978347f66f945b77");
        BodyVO body = new BodyVO(100000001L, "yangcan", new Date());

        String sign = SignUtil.sign("sdfsdfdsfdsfds", url, headerMap, body);
        System.out.println(sign);
    }

    /**
     * 获取当前时间的UTC格式
     */
    private static String generateDate() {
        Date now = new Date();
        DateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.US);
        return format.format(now);
    }

    @Data
    @AllArgsConstructor
    public static class BodyVO {
        private Long ycId;
        private String ycName;
        private Date ycTime;
    }
}

输出:

sign = 4f52eb34b30129a8d511dc803044086b

到此这篇关于Java实现API sign签名校验的方法详解的文章就介绍到这了,更多相关Java API签名校验内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 接口签名怎么用Java实现

    java实现接口签名 为了保证数据传输的安全性,跟其他系统进行数据交互时,双方应该约定好密钥,把数据进行加密,接口签名,这样双方调用接口时,验证接口签名一致时就表明数据传输过程中没有被修改. 后端数据签名主要代码: 控制器: @Controller public class SignController { @Autowired private ISignService signService; /** * 验证接口签名 * @param dto * @return */ @RequestMap

  • 详解Java接口签名(Signature)实现方案

    目录 一.要求 二.流程 三.实现 大家好,我是程序员田同学! 今天上午收到一个需求,针对当前的系统开发一个对外开放的接口. 既然是对外开放,那么调用者一定没有我们系统的Token,就需要对调用者进行签名验证,签名验证采用主流的验证方式,采用Signature 的方式. 一.要求 下图为具体要求 二.流程 ​1.线下分配appid和appsecret,针对不同的调用方分配不同的appid和appsecret 2.加入timestamp(时间戳),10分钟内数据有效 3.加入流水号noncestr

  • 基于JAVA的短信验证码api调用代码实例

    本文实例为大家分享了JAVA的短信验证码api调用代码,供大家参考,具体内容如下 import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import jav

  • Java使用云片API发送短信验证码

    下面开始介绍的是如何利用机器完成批量操作,将短信业务自动化. 获取APIKEY 云片网提供了完整的SDK和API,可以帮助开发者快速完成业务开发. 在开始Coding前,需要先获取APIKEY,如下所示. 获取APIKEY 点击眼睛按钮后,输入验证码,就可以查看APIKEY了. 这里需要说明的是,APIKEY特别重要,一定要保护好它,避免泄露.云片这边提供了几重保护机制,例如验证.敏感处理.子账号独立APIKEY等,看得出来他们的安全意识还是挺不错的. 开始Coding 有了APIKEY,就可以

  • Java实现抠图片文字或签名的完整代码

    目录 java抠图片文字或签名 运行原理 完整代码 java抠图片文字或签名 运行原理 第一步 遍历像素点 BufferedImage image = ImageIO.read(new File(input)); // 图片透明度 int alpha = 0; // 最小 int maxX = 0, maxY = 0; // 最大 int minX = image.getWidth(), minY = image.getHeight(); for (int y = image.getMinY()

  • Java实现API sign签名校验的方法详解

    目录 1. 前言 2. 签名生成策略 3. API 签名算法 Java 实现 4. 测试一下 1. 前言 目的:为防止中间人攻击. 场景: 项目内部前后端调用,这种场景只需要做普通参数的签名校验和过期请求校验,目的是为了防止攻击者劫持请求 url 后非法请求接口. 开放平台向第三方应用提供能力,这种场景除了普通参数校验和请求过期校验外,还要考虑 3d 应用的授权机制,不被授权的应用就算传入了合法的参数也不能被允许请求成功. 2. 签名生成策略 接下来详述场景 2,其实场景 1 也包含在场景 2

  • Java JWT实现跨域身份验证方法详解

    目录 1.JWT简介 2.JWT的结构 2.1 头部(header) 2.2 载荷(payload) 2.3 签证(signature) 3.JWT的原则 4.JWT的用法 5.JWT的问题和趋势 6.整合JWT令牌 6.1 在模块中添加jwt工具依赖 6.2 创建JWT工具类 1.JWT简介 JWT(JSON Web Token)是目前流行的跨域认证解决方案,是一个开放标准(RFC 7519),它定义了一种紧凑的.自包含的方式,用于作为JSON对象在各方之间安全地传输信息.该信息可以被验证和信

  • SpringBoot进行参数校验的方法详解

    目录 介绍 1.SpringBoot中集成参数校验 1.1引入依赖 1.2定义参数实体类 1.3定义校验类进行测试 1.4打开接口文档模拟提交数据 2.参数异常加入全局异常处理器 3.自定义参数校验 3.1创建自定义注解 3.2自定义校验逻辑 3.3在字段上增加注解 3.4体验效果 4.分组校验 4.1定义分组接口 4.2在模型中给参数分配分组 4.3体现效果 介绍 在日常的接口开发中,为了防止非法参数对业务造成影响,经常需要对接口的参数进行校验,例如登录的时候需要校验用户名和密码是否为空,添加

  • Java利用StampedLock实现读写锁的方法详解

    目录 概述 StampedLock介绍 演示例子 性能对比 总结 概述 想到读写锁,大家第一时间想到的可能是ReentrantReadWriteLock.实际上,在jdk8以后,java提供了一个性能更优越的读写锁并发类StampedLock,该类的设计初衷是作为一个内部工具类,用于辅助开发其它线程安全组件,用得好,该类可以提升系统性能,用不好,容易产生死锁和其它莫名其妙的问题.本文主要和大家一起学习下StampedLock的功能和使用. StampedLock介绍 StampedLock的状态

  • SpringBoot使用swagger生成api接口文档的方法详解

    目录 前言 具体例子 maven配置 项目application.yml配置 springApplication添加swagger注解 在控制层添加swagger注解 前言 在之前的文章中,使用mybatis-plus生成了对应的包,在此基础上,我们针对项目的api接口,添加swagger配置和注解,生成swagger接口文档 具体可以查看本站spring boot系列文章: spring boot项目使用mybatis-plus代码生成实例 具体例子 maven配置 在使用之前,我们需要添加s

  • Java使用POI实现导出Excel的方法详解

    目录 一.前景 二.概念 2.1. 简介 2.2.Excel版本和相关对象 2.3.WorkBook 2.4.POI依赖 三.POI - 写 3.1.代码示例 3.2. 性能对比 3.3. 测试rowAccessWindowSize 3.4. 导出Excel样式设置 四.POI - 读 4.1.代码示例 4.2.读取不同的数据类型 4.3.读取公式 五.POI - 遇到的坑 一.前景 在项目开发中往往需要使用到Excel的导入和导出,导入就是从Excel中导入到DB中,而导出就是从DB中查询数据

  • Java操作Excel文件解析与读写方法详解

    目录 一.概述 二.Apache POI 三.XSSF解析Excel文件 1.Workbook(Excel文件) 2.Sheet(工作簿) 3.Row(数据行) 4.Cell(单元格) 四.超大Excel文件读写 1.使用POI写入 2.使用EasyExcel 一.概述 在应用程序的开发过程中,经常需要使用 Excel 文件来进行数据的导入或导出.所以,在通过Java语言实现此 类需求的时候,往往会面临着Excel文件的解析(导入)或生成(导出). 在Java技术生态圈中,可以进行Excel文件

  • Java 添加超链接到 Word 文档方法详解

    在Word文档中,超链接是指在特定文本或者图片中插入的能跳转到其他位置或网页的链接,它也是我们在编辑制作Word文档时广泛使用到的功能之一.今天这篇文章就将为大家演示如何使用Free Spire.Doc for Java在Word文档中添加文本超链接和图片超链接. Jar包导入 方法一:下载Free Spire.Doc for Java包并解压缩,然后将lib文件夹下的Spire.Doc.jar包作为依赖项导入到Java应用程序中. 方法二:通过Maven仓库安装JAR包,配置pom.xml文件

  • Java Fluent Mybatis 聚合查询与apply方法详解流程篇

    前言 接着上一篇文章:Java Fluent Mybatis 分页查询与sql日志输出详解流程篇 我把分页已经调整好了,现在实验一下官方给出的聚合查询方法. GitHub代码仓库:GitHub仓库 数据准备 为了聚合查询的条件,添加了几条数据. MIN 我们试着获取最小的年龄. 方法实现 @Override public Integer getAgeMin() { Map<String, Object> result = testFluentMybatisMapper .findOneMap(

  • Java实现字符串转为驼峰格式的方法详解

    字符串转为驼峰格式 构建工具类 package com.yt.common.util; import com.yt.common.dto.NameCode; import com.yt.exam.enums.ZyEnum; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 字符串转为驼峰格式 构建工具类 * @author LYY * @date 2022/07

随机推荐