接口签名规则及Java代码demo实现

签名规则
签名生成的通用步骤如下:
第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特别注意以下重要规则:
◆ 参数名ASCII码从小到大排序(字典序);
◆ 如果参数的值为空不参与签名;
◆ 参数名区分大小写;
◆ 验证接口调用传送的sign参数不参与签名,将生成的签名与该sign值作校验。
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。 注意:密钥的长度为32个字节。

1.导入jar
implementation("commons-beanutils:commons-beanutils:1.9.3")

2.MD5工具类

import java.security.MessageDigest;

public class MD5 {
private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "a", "b", "c", "d", "e", "f"}; /**
* 转换字节数组为16进制字串
* @param b 字节数组
* @return 16进制字串
*/
public static String byteArrayToHexString(byte[] b) {
StringBuilder resultSb = new StringBuilder();
for (byte aB : b) {
resultSb.append(byteToHexString(aB));
}
return resultSb.toString();
} /**
* 转换byte到16进制
* @param b 要转换的byte
* @return 16进制格式
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
} /**
* MD5编码
* @param origin 原始字符串
* @return 经过MD5加密之后的结果
*/
public static String MD5Encode(String origin) {
String resultString = null;
try {
resultString = origin;
MessageDigest md = MessageDigest.getInstance("MD5");
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
} catch (Exception e) {
e.printStackTrace();
}
return resultString;
} }

3.实体类

import java.util.List;

public class UploadReqVO {
//手机号
private String mobile;
//姓名
private String realname;
//身份证号
private String idno;
//字符数组
private List<String> testBase64Str;
private String sign; public String getMobile() {
return mobile;
} public void setMobile(String mobile) {
this.mobile = mobile;
} public String getRealname() {
return realname;
} public void setRealname(String realname) {
this.realname = realname;
} public String getIdno() {
return idno;
} public void setIdno(String idno) {
this.idno = idno;
} public List<String> getTestBase64Str() {
return testBase64Str;
} public void setTestBase64Str(List<String> testBase64Str) {
this.testBase64Str = testBase64Str;
} public String getSign() {
return sign;
} public void setSign(String sign) {
this.sign = sign;
}
}

4.签名类及测试类

import com.alibaba.fastjson.JSON;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.tomcat.util.security.MD5Encoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*; public class Signature {
private static Logger log = LoggerFactory.getLogger(Signature.class); /**
* 对象入参,对象转MAP
* @param object
* @return
* @throws Exception
*/
public static String getSignNew(Object object,String key) throws Exception {
Map<String, String> map = BeanUtils.describe(object);
return getSignNew(map,key);
} public static String getSignNewArray(UploadReqVO object, String key) throws Exception {
Map<String, String> map = BeanUtils.describe(object);
//覆盖
map.put("testBase64Str", JSON.toJSONString(object.getTestBase64Str()).replace("\"",""));
return getSignNew(map,key);
} /**
* 签名
* @param map
* @return
*/
private static String getSignNew(Map<String,String> map,String key) throws Exception{
ArrayList<String> list = new ArrayList<String>();
for(Map.Entry<String,String> entry:map.entrySet()){
if(entry.getValue() != null && !StringUtils.isEmpty(entry.getValue().toString()) && !"null".equals(entry.getValue())
&& !"class".equals(entry.getKey())){ //空字符串 entry.getValue()!=""){
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
// Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER); //忽略大小写
Arrays.sort(arrayToSort);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
//过滤最后一个字符串&
int lastIdx = result.lastIndexOf("&");
result = result.substring(0,lastIdx);
// result += "key=" + key; //去掉key= 字符串
result += key; //key直接拼接在后面 try{
log.info("Sign Before MD5:"+ result);
result = MD5.MD5Encode(result);
log.info("Sign Result:" + result);
}catch (Exception e) {
e.printStackTrace();
} return result;
} public static boolean checkIsSignValidFromResyponseStringObject(Object object,String key) throws Exception {
Map<String, String> map = org.apache.commons.beanutils.BeanUtils.describe(object);
return checkIsSignValidFromResponseString(map,key);
} public static boolean checkIsSignValidFromResponseStringArray(UploadReqVO object, String key) throws Exception {
Map<String, String> map = BeanUtils.describe(object);
//覆盖
map.put("testBase64Str", JSON.toJSONString(object.getTestBase64Str()).replace("\"",""));
return checkIsSignValidFromResponseString(map,key);
} /**
* object转换为map 验证签名
* @param map
* @return
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
* @throws IllegalAccessException
*/
private static boolean checkIsSignValidFromResponseString(Map<String,String> map,String key) throws Exception {
String signFromAPIResponse = null;
if(map.get("sign")!=null){
signFromAPIResponse = map.get("sign").toString();
} if(signFromAPIResponse=="" || signFromAPIResponse == null){
log.info("signFromAPIResponse报空");
return false;
} //清掉返回数据对象里面的Sign数据(不能把这个数据也加进去进行签名),然后用签名算法进行签名
map.put("sign","");
map.put("class","");
//将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
//重新签名
log.info("签名前的map="+map);
String signForAPIResponse = Signature.getSignNew(map,key);
log.info("签名后的字符串="+signForAPIResponse); if(!signForAPIResponse.equals(signFromAPIResponse)){
//签名验不过,表示这个API返回的数据有可能已经被篡改了
log.info("匹配不一致");
return false;
}
return true;
} public static void main(String[] args) {
String key = "testkey123testkey123testkey12345"; try {
UploadReqVO uploadReqVo = new UploadReqVO(); List<String> pic1List = new ArrayList<String>();
String pic5 = Base64.getEncoder().encodeToString("测试字符串1".getBytes(StandardCharsets.UTF_8));
pic1List.add(pic5);
String pic6 = Base64.getEncoder().encodeToString("测试字符串2".getBytes(StandardCharsets.UTF_8));
pic1List.add(pic6);
uploadReqVo.setTestBase64Str(pic1List); uploadReqVo.setIdno("465601200810081204");
uploadReqVo.setRealname("测试员");
uploadReqVo.setMobile("19945558899");
uploadReqVo.setSign("");
//数组方法
String signStr3 = Signature.getSignNewArray(uploadReqVo,key);
//非数组方法
// String signStr3 = Signature.getSignNew(uploadReqVo,key);
System.out.println("(上传图片)签名字符串:" + signStr3);
uploadReqVo.setSign(signStr3);
System.out.println("(上传图片)参数json=" + JSON.toJSONString(uploadReqVo)); //验证签名
//数组方法
boolean flag3 = Signature.checkIsSignValidFromResponseStringArray(uploadReqVo,key);
//非数组方法
// boolean flag3 = Signature.checkIsSignValidFromResyponseStringObject(uploadReqVo,key);
System.out.println("(上传图片)验证签名是否一致="+flag3); } catch (Exception e) {
e.printStackTrace();
} }
}
打印输出日志对比:
//数组方式
16:56:50.014 [main] INFO com.example.utils.Signature - Sign Before MD5:idno=465601200810081204&mobile=19945558899&realname=测试员&testBase64Str=[5rWL6K+V5a2X56ym5LiyMQ==,5rWL6K+V5a2X56ym5LiyMg==]testkey123testkey123testkey12345
16:56:50.061 [main] INFO com.example.utils.Signature - Sign Result:61867a7f32594eec1967fcddea8d96c3
(上传图片)签名字符串:61867a7f32594eec1967fcddea8d96c3
(上传图片)参数json={"idno":"465601200810081204","mobile":"19945558899","realname":"测试员","sign":"61867a7f32594eec1967fcddea8d96c3","testBase64Str":["5rWL6K+V5a2X56ym5LiyMQ==","5rWL6K+V5a2X56ym5LiyMg=="]} 16:56:50.089 [main] INFO com.example.utils.Signature - 签名前的map={testBase64Str=[5rWL6K+V5a2X56ym5LiyMQ==,5rWL6K+V5a2X56ym5LiyMg==], mobile=19945558899, sign=, class=, idno=465601200810081204, realname=测试员}
16:56:50.089 [main] INFO com.example.utils.Signature - Sign Before MD5:idno=465601200810081204&mobile=19945558899&realname=测试员&testBase64Str=[5rWL6K+V5a2X56ym5LiyMQ==,5rWL6K+V5a2X56ym5LiyMg==]testkey123testkey123testkey12345
16:56:50.089 [main] INFO com.example.utils.Signature - Sign Result:61867a7f32594eec1967fcddea8d96c3
16:56:50.089 [main] INFO com.example.utils.Signature - 签名后的字符串=61867a7f32594eec1967fcddea8d96c3
(上传图片)验证签名是否一致=true //非数组方法,弊端是:testBase64Str构建签名字符串的时候,默认取数组的第一个字符,而不是[]结构的全部数据。
16:59:15.692 [main] INFO com.example.utils.Signature - Sign Before MD5:idno=465601200810081204&mobile=19945558899&realname=测试员&testBase64Str=5rWL6K+V5a2X56ym5LiyMQ==testkey123testkey123testkey12345
16:59:15.697 [main] INFO com.example.utils.Signature - Sign Result:bfc9246143246f1852b4a29732aabcf6
(上传图片)签名字符串:bfc9246143246f1852b4a29732aabcf6
(上传图片)参数json={"idno":"465601200810081204","mobile":"19945558899","realname":"测试员","sign":"bfc9246143246f1852b4a29732aabcf6","testBase64Str":["5rWL6K+V5a2X56ym5LiyMQ==","5rWL6K+V5a2X56ym5LiyMg=="]} 16:59:15.775 [main] INFO com.example.utils.Signature - 签名前的map={testBase64Str=5rWL6K+V5a2X56ym5LiyMQ==, mobile=19945558899, sign=, class=, idno=465601200810081204, realname=测试员}
16:59:15.775 [main] INFO com.example.utils.Signature - Sign Before MD5:idno=465601200810081204&mobile=19945558899&realname=测试员&testBase64Str=5rWL6K+V5a2X56ym5LiyMQ==testkey123testkey123testkey12345
16:59:15.775 [main] INFO com.example.utils.Signature - Sign Result:bfc9246143246f1852b4a29732aabcf6
16:59:15.775 [main] INFO com.example.utils.Signature - 签名后的字符串=bfc9246143246f1852b4a29732aabcf6
(上传图片)验证签名是否一致=true

接口签名规则及Java代码demo实现的更多相关文章

  1. 微信支付接口--超详细带注释代码--Demo

    如果本文对你有用,请爱心点个赞,提高排名,帮助更多的人.谢谢大家!❤ 如果解决不了,可以在文末进群交流. 如果对你有帮助的话麻烦点个[推荐]~最好还可以follow一下我的GitHub~感谢观看! 微 ...

  2. 四种java代码静态检查工具

    [转载]常用 Java 静态代码分析工具的分析与比较 转载自 开源中国社区 http://www.oschina.net/question/129540_23043       1月16日厦门 OSC ...

  3. API服务接口签名代码与设计,如果你的接口不走SSL的话?

    在看下面文章之前,我们先问几个问题 rest 服务为什么需要签名? 签名的几种方式? 我认为的比较方便的快捷的签名方式(如果有大神持不同意见,可以交流!)? 怎么实现验签过程 ? 开放式open ap ...

  4. [改善Java代码]在接口中不要存在实现代码

    第3章  类.对象及方法 书读得多而不思考,你会觉得自己知道的很多. 书读得多而思考,你会觉得自己不懂的越来越多. —伏尔泰 在面向对象编程(Object-Oriented Programming,O ...

  5. java接口签名(Signature)实现方案续

    一.前言 由于之前写过的一片文章 (java接口签名(Signature)实现方案 )收获了很多好评,此次来说一下另一种简单粗暴的签名方案.相对于之前的签名方案,对body.paramenter.pa ...

  6. java接口签名(Signature)实现方案

    预祝大家国庆节快乐,赶快迎接美丽而快乐的假期吧!!! 一.前言 在为第三方系统提供接口的时候,肯定要考虑接口数据的安全问题,比如数据是否被篡改,数据是否已经过时,数据是否可以重复提交等问题.其中我认为 ...

  7. 规则引擎集成接口(九)Java类对象

    Java类对象 右键点击“对象库” —“添加java类对象”,如下图: 弹出窗体,在文本框中输入类的全名“com.flagleader.test.Test”,选择该类型后确定,如下: 显示如下,勾选上 ...

  8. Java代码签名证书申请和使用指南

    第1步 下载签名工具 Step 1: Download Signing Tools 如果您还没有签名工具,请到SUN公司网站免费下载:http://java.sun.com/j2se/,推荐下载JDK ...

  9. 论坛源码推荐(11.6):iPhone6/6 plus屏幕适配Demo,Java代码转Objective-C

    http://www.cocoachina.com/ios/20141106/10153.html iPhone6/6 plus 屏幕适配Demo(代码底层处理)(论坛会员satian)htt 该项目 ...

  10. 接口测试-Java代码实现接口请求并封装

    前言:在接口测试和Java开发中对接口请求方法进行封装都非常有必要,无论是在我们接口测试的时候还是在开发自测,以及调用某些第三方接口时,都能为我们调用和调试接口提供便捷: Java实现对http请求的 ...

随机推荐

  1. Serverless 应用优化四则秘诀

    ​简介:Serverless 架构下,虽然我们更多精力是关注我们的业务代码,但是实际上对于一些配置和成本也是需要进行关注的,并且在必要的时候,还需要根据配置与成本进行对我们的 Serverless 应 ...

  2. Serverless 工程实践 | Serverless 应用开发观念的转变

    ​简介: Serverless 架构带来的除了一种新的架构.一种新的编程范式,还包括思路上的转变,尤其是开发过程中的一些思路转变.有人说要把 Serverless 架构看成一种天然的分布式架构,需要用 ...

  3. [FAQ] Error: com.mysql.jdbc.Driver not loaded. :jdbc_driver_library

    以上问题出现在 logstash.conf 未配置好 MySQL 的 JDBC 驱动时导致的错误提示. 首先,下载好 MySQL JDBC 驱动库,可以放到 logstash.conf 所在当前目录或 ...

  4. dotnet C# 序列化 XML 时进行自动格式化

    默认的序列化对象为 XML 字符串时,是没有进行格式化的,也就是所有的内容都在相同的一行.本文告诉大家方法,在序列化对象时,转换的 XML 是格式化的.或者说拿到 XML 字符串,对这个 XML 字符 ...

  5. Android Studio自强迫升级到4.2版本后调试Native项目时总是卡死问题

    原文地址:https://www.zhaimaojun.top/Note/5464968 就在昨天,也就是2021年5月6号,Android Studio强迫用户升级到4.2版本, 原因就是jcent ...

  6. Javascript返回顶部和砸金蛋,跑马灯等游戏代码实现

    1. 我们经常写页面的时候会遇到页面很长需要做返回顶部的操作:$("id /class").animate({scrollTop:$('.class').offset().top} ...

  7. centos7实现多网卡多线路

    移动线路IP:179.15.5.253 网卡配置内容: TYPE=Ethernet PROXY_METHOD=none BROWSER_ONLY=no BOOTPROTO=static DEFROUT ...

  8. Windows有自带的远程桌面 为啥还要商业远程桌面

    网上有一类观点:最好的远程桌面就是windows自带的远程桌面. 那我们打破砂锅问到底,亲手实践下看看. 首先,我们来到了windows官网-远程桌面介绍页面. 如何使用远程桌面 设置你想要连接以使其 ...

  9. Vue3.x+springboot集成pageoffice

    说明:由于pageoffice浏览器是ie内核,vue3不兼容ie.所以需要把页面放在后端 一,前端项目: 1.index.html页面引用pageoffice.js <script type= ...

  10. 训练营 |【AIRIOT大学计划暑期训练营】第三期即将开营,报名从速!

    培养新生力量,聚焦产业融合.为了促进物联网产业的纵深发展和创新,推进教育链.产业链与创新链的有机衔接,提高学生理论.实践和创新能力,为行业培养更多优秀人才,航天科技控股集团股份有限公司将于2023年7 ...