Java API接口签名认证

我们在进行程序开发的时候,一定会开发一些API接口,供他人访问。当然这些接口中有可能是开放的,也有可能是需要登录才能访问的,也就是需要Token鉴权成功后才可以访问的。那么问题来了,我们这些开放的接口,难道不是一直暴露在外吗?该如何来保证这些接口的安全性呢?
本编文章,将通过API接口签名认证的方式来解决以上问题。

什么是接口签名认证

这个可以看成,比如,我申请了微信公众号或者小程序,公众号的基本信息中就会包含AppId和AppSecret两个数据。这两个数据需要我们用户进行保存,尤其是AppSecret更不能暴露在外,以保证安全。当我们需要请求微信进行授权登录的时候,我们需要根据微信的规则拼接请求链接,其中请求链接中会包含AppId,AppSecret等的一些信息,通过按规则拼接好的链接则可以成功请求微信,否则会请求失败。
而我们要做的就是根据微信的这个原理,实现自己程序的API接口签名验证。

接口签名参数规则

需要在每次请求的header中携带以下参数:

  1. appKey:相当于appId,一个请求来源的标识。
  2. sign:签名,由签名规则计算而来。
  3. t:时间戳,通过计算此时间戳与服务器当前时间差来防止请求重放问题。

签名(sign)计算公式、规则:

sign=MD5(data+AppSecret+t)
其中data为请求参数的拼接,其规则如下:

  1. path传参形式:如/api/user/{userId}/{mobile},单个或多个参数,按地址中参数的位置排序。则data=userId+mobile的字符串拼接。
  2. 对象形式传参,即json形式:需要按对象中的属性进⾏字典升序排序,然后对其属性值按此顺序进⾏拼接。如User类如下
@Data
class User{private String userId;private String mobile;
}

则data的计算为:userId,mobile两个属性名按字典排序。顺序为mobile->userId,如果mobile=17612345678;userId=123,则data的拼接顺序为 17612345678123。而sign=MD5(17612345678123+AppSecret+t)。问号拼接参数同理。
3. list形式传参:需要将list⾥⾯的内容进⾏依次拼接。

代码实现

以下代码中包含一些自己造的工具类,如AssertUtils,LocalCacheUtils等。

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.stream.Stream;@Slf4j
@Component
public class VerifySignUtils {@Autowiredprivate RedisRepository redisRepository;// 设置请求重放的时间差private final long SIGN_EXPIRE = 1000 * 10;public boolean verifySign(String appKey, String sign, String t, Object object) {// 参数判空,校验log.info("传入的时间戳:{}", t);if (null == appKey) {log.error("没有传入appKey");return false;}long now = System.currentTimeMillis();// 校验请求重放if (Long.parseLong(t) < now - SIGN_EXPIRE) {log.error("sign失效!传入时间戳{};当前时间戳:{}", t, now);return false;}// 取出缓存的AppId和AppSecret配置表,因为是对接多个程序,是以配置表的形式实现的Map<Integer, String> appInfos = Optional.ofNullable(LocalCacheUtils.get(BaseConstants.APP_CACHE_NAME)).map(it -> (Map<Integer, String>) it).orElseGet(() ->(Map<Integer, String>) LocalCacheUtils.setExpire(BaseConstants.APP_CACHE_NAME, Optional.ofNullable(redisRepository.get(BaseConstants.APP_CACHE_NAME)).map(it -> (Map<Integer, String>) it).orElseGet(null),BaseConstants.LOCAL_CACHE_APP_INFO_EXPIRE));String secret = appInfos.get(Integer.parseInt(appKey));if (null == secret) {log.error("appKey错误");return false;}// 根据请求,计算拼接参数,即data的拼接String objectFields;if (object instanceof Object[]) {StringBuilder builder = new StringBuilder();for (Object o : ((Object[]) object)) {builder.append(o);}objectFields = builder.toString();} else if (object instanceof List) {StringBuilder builder = new StringBuilder();((List) object).forEach(it -> builder.append(getObjectFields(it)));objectFields = builder.toString();} else if (object instanceof String || object instanceof Long || object instanceof Integer || object instanceof Boolean) {objectFields = object.toString();} else {objectFields = getObjectFields(object);}log.info("参数按顺序拼接:{}", objectFields);// 计算sign签名的值String tempSign = Md5Utils.getMD5((objectFields + secret + t).getBytes()).toUpperCase();log.info("计算出的sign:{}\t传入的sign:{}", tempSign, sign);// 校验传入的签名和服务端计算的签名是否一致,不一致则,签名认证失败if (!tempSign.equals(sign)) {log.error("计算验签与传入的验签不符");return false;}return true;}// 以下为通过反射拼接对象参数的方法private String getObjectFields(Object object) {final Field[] fields = object.getClass().getDeclaredFields();final TreeMap<String, Object> treeMap = new TreeMap<>();Stream.of(fields).map(Field::getName).forEach(it -> treeMap.put(it, getFieldValueByName(it, object)));final StringBuilder builder = new StringBuilder();treeMap.forEach((k, v) -> builder.append(v));return builder.toString();}private Object getFieldValueByName(String fieldName, Object o) {try {String firstLetter = fieldName.substring(0, 1).toUpperCase();String getter = "get" + firstLetter + fieldName.substring(1);final Method method = o.getClass().getMethod(getter, new Class[]{});final Object value = method.invoke(o, new Object[]{});if (null == value) {return "";}if (value instanceof List) {return ((List) value).stream().map(it -> {if (it instanceof String || it instanceof Long || it instanceof Integer || it instanceof Boolean) {return it;} else {return getObjectFields(it);}}).reduce((it1, it2) -> it1 + "" + it2).get().toString();}return value;} catch (Exception e) {return "";}}
}

以上代码并不固定,也可根据自己的签名规则进行改造配置。

调用代码如下,通过对Controller配置AOP的形式进行调用:

@Aspect
@Slf4j
@Configuration
public class LogRecordAspect {@Autowiredprivate VerifySignUtils verifySignUtils;@Autowiredprivate AopLogUtil aopLogUtil;@Value("${spring.profiles.active}")private String active;private static final List<String> activeList = Arrays.asList("prod","test");ThreadLocal<Long> startTime = new ThreadLocal<Long>();@Pointcut("execution(* com.xx.xx.xx.controller..*.*(..))")public void webLog() {}@Before("webLog()")public void doBefore(JoinPoint joinPoint) {startTime.set(System.currentTimeMillis());ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();List<Object> collect = aopLogUtil.verifySignLog(joinPoint);String appKey = request.getHeader("appKey");String sign = request.getHeader("sign");String t = request.getHeader("t");boolean flag;// get请求与其它请求进行区分,如果有delete,put请求,需要另加if ("GET".equals(request.getMethod())) {flag = verifySignUtils.verifySign(appKey, sign, t, collect.toArray());} else {flag = verifySignUtils.verifySign(appKey, sign, t, collect.get(0));}if (!flag && activeList.contains(active)) {log.error("验签失败!");throw new BaseException(R.SERVICE_VERIFY_SIGN_ERROR, "");}}@AfterReturning(returning = "ret", pointcut = "webLog()")public void doAfterReturning(Object ret) {// 处理完请求,返回内容log.warn("开始响应:RESPONSE: {} ", ret);log.warn("响应时间: {} ms", System.currentTimeMillis() - startTime.get());}
}

Java API接口签名认证相关推荐

  1. 开放平台设计之接口签名认证

    目录 前言 签名认证 签名认证步骤: 下面以java代码举例: DEMO 前言 当前时代,数据是王 道!当我们自己的平台有了足够大的数据量,就有可能诞生一个开放平台供第三方分析.使用.那么我们怎么去实 ...

  2. API接口签名生成算法和签名验证算法

    1.参考网上资料和书本资料,实现了API接口签名生成算法和签名验证算法. (1)参考资料:https://www.jianshu.com/p/d47da77b6419 (2)参考书籍:高级软件架构师教 ...

  3. API 接口签名验签

    目录 一.为什么需要 API 接口签名 二.API 接口签名验签实现机制 一.为什么需要 API 接口签名 对外开放的 API 接口都会面临一些安全问题,例如伪装攻击.篡改攻击.重放攻击以及数据信息泄 ...

  4. Java—通过sign签名认证实现安全的开放接口API

    关注微信公众号:CodingTechWork,一起学习进步. 文章目录 引言 API接口 timestamp保证唯一性 shell生成timestamp java生成timestamp模板 sign签 ...

  5. API密钥签名认证详解,包含timestamp+nonce方案BY:Zz Apollo

    本文举例来说明API签名,并有具体实现流程,规则弄会,一通百通. 本文先用一个故事举例,方便理解,然后对整个流程做了逐步分析和局部代码实现,最后把代码整合起来,想直接看整合后代码的可以直接去最底. 一 ...

  6. request参数升序排序 md5加密 防重播 header信息 java API接口调用 切片机制实现

    api接口大多都支持访问信息的验证,其中参数的排序,加密都是经常用到的.有时候还需要将验证信息放到header中. 将api调用者的参数的key及头信息(时间戳.随机串,调用者标识)按照ascii码升 ...

  7. java api接口报500_应用程序编程接口API,我们来聊一聊这个熟悉的名词

    API,全称叫做Application Programming interface,也就是应用程序接口,API是一些预先定义的函数,我是学Java的,当我要使用这些函数的时候,便可以直接调用Java ...

  8. java api接口怎么写_Java 如何设计 API 接口,实现统一格式返回?

    来源:老顾聊技术 前言 接口交互 返回格式 控制层Controller 美观美化 优雅优化 实现方案 前言 在移动互联网,分布式.微服务盛行的今天,现在项目绝大部分都采用的微服务框架,前后端分离方式, ...

  9. 接口模糊测试工具java,api 接口 fuzz 测试初探

    Alt pic 目标 在日常测试工作中,经常会有api接口的测试,除了正向流程的测试之外,我们经常还需要覆盖一些异常情况. 例如: 不合法字符串 字符串超长 应该是数字类型的,传入了字母 参数为空 传 ...

最新文章

  1. Science重磅!人类特有基因触发猴子长出更强大的大脑
  2. 设计模式-结构性模式
  3. spring核心之IOC
  4. 立足前沿 直击热点 搭建平台,2018中国人工智能大会在深圳拉开帷幕
  5. x64 stack walking、调用约定、函数参数识别
  6. 数据库字段属性配置工具界面[用于代码生成]
  7. JAVA多线程,真的能提高效率吗
  8. go 的基本数据类型
  9. 华为笔记本没有网线口_3599元起,华为台式机MateStation B515上架:R5 4600G
  10. pb 执行insert 后return是否会自动提交_一条MySQL更新语句是怎么执行的?
  11. excel表格显示无法连接服务器,excel中表格无法连接数据库-EXCEL 连接SQL SERVER数据库显示无法连接...
  12. 安装ROS中出现bash: /opt/ros/melodic/setup.bash: 没有那个文件或目录或者bash: /opt/ros/kinetic/setup.bash:的解决办法
  13. 基于ThinkPHP的校园网上订餐系统设计与实现
  14. 不用加减乘除做四则运算
  15. 单细胞分析:聚类流程(六)
  16. paypal html5 支付,uniapp 对接 paypal支付 (h5,app端)
  17. png转成SVG方法
  18. 信号完整性分析学习--12--IBIS模型
  19. 3.7计算机网络(IP数据报格式,IP数据报分片,IPv4)
  20. python 写文本文件出现乱码

热门文章

  1. xss(Cross Site Scripting)
  2. overflow滚动条样式
  3. 沪股通连续八日净流入 累计净流入58.67亿元
  4. 计算机发展史简介(详细)
  5. 【Java】读取单颗北斗卫星导航星历文件进行卫星位置计算(RENIX3.04)
  6. 2023重庆邮电大学计算机专硕(803)在职考研初试经验贴
  7. BurpSuite安装插件教程:BurpCrypto: 万能网站密码爆破测试工具
  8. 2018双一流排名 计算机,2018中国双一流学科排名出炉,北京大学第一
  9. 计算机通信电气电子适合女生学,工科中适合女生的专业和不适合女生的专业有哪些...
  10. contentEditable与suppressContentEditableWarning