前言

后端在写对外的API接口时,一般会对参数进行签名来保证接口的安全性,在设计签名算法的时候,主要考虑的是这几个问题:
1. 请求的来源是否合法
2. 请求参数是否被篡改
3. 请求的唯一性
我们的签名加密也是主要针对这几个问题来实现

设计

基于上述的几个问题,我们来通过已下步骤来实现签名加密:
1. 通过分配给APP对应的app_key和app_secret来验证身份
2. 通过将请求的所有参数按照字母先后顺序排序后拼接再MD5加密老保证请求参数不被篡改
3. 请求里携带时间戳参数老保证请求的唯一和过期,重复的请求在指定时间(可配置)内有效

实现

  1. 签名生成:

    1. 生成当前时间戳timestamp=now
    2. 按照请求参数名的字母升序排列非空请求参数(包含accessKey)stringA="AccessKey=access&home=world&name=hello&work=java&timestamp=now&nonce=random";
    3. 拼接密钥accessSecretstringSignTemp="AccessKey=access&home=world&name=hello&work=java&timestamp=now&nonce=random&accessSecret=secret";
    4. MD5并转换为大写生成签名 sign=MD5(stringSignTemp).toUpperCase();

JAVA代码如下:params是从request里面获取的所有参数map,accessSecret是加密密钥

 private String createSign(Map<String, Object> params, String accessSecret) throws UnsupportedEncodingException {Set<String> keysSet = params.keySet();Object[] keys = keysSet.toArray();Arrays.sort(keys);StringBuilder temp = new StringBuilder();boolean first = true;for (Object key : keys) {if (first) {first = false;} else {temp.append("&");}temp.append(key).append("=");Object value = params.get(key);String valueString = "";if (null != value) {valueString = String.valueOf(value);}temp.append(valueString);}temp.append("&").append(ACCESS_SECRET).append("=").append(accessSecret);return MD5Util.MD52(temp.toString()).toUpperCase();}
  1. 签名校验:

    • 参数格式校验
    • 超时校验
    • 验证签名
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Map<String, Object> result = new HashMap<String, Object>();String timestamp = request.getParameter(TIMESTAMP_KEY);String accessKey = request.getParameter(ACCESS_KEY);String accessSecret = map.get(accessKey);if (!org.apache.commons.lang.StringUtils.isNumeric(timestamp)) {result.put("code", 1000);result.put("msg", "请求时间戳不合法");WebUtils.writeJsonByObj(result, response, request);return false;}// 检查KEY是否合理if (StringUtils.isEmpty(accessKey) || StringUtils.isEmpty(accessSecret)) {result.put("code", 1001);result.put("msg", "加密KEY不合法");WebUtils.writeJsonByObj(result, response, request);return false;}Long ts = Long.valueOf(timestamp);// 禁止超时签名if (System.currentTimeMillis() - ts > SIGN_EXPIRED_TIME) {result.put("code", 1002);result.put("msg", "请求超时");WebUtils.writeJsonByObj(result, response, request);return false;}if (!verificationSign(request, accessKey, accessSecret)) {result.put("code", 1003);result.put("msg", "签名错误");WebUtils.writeJsonByObj(result, response, request);return false;}return true;}

校验签名

 private boolean verificationSign(HttpServletRequest request, String accessKey, String accessSecret) throws UnsupportedEncodingException {Enumeration<?> pNames = request.getParameterNames();Map<String, Object> params = new HashMap<String, Object>();while (pNames.hasMoreElements()) {String pName = (String) pNames.nextElement();if (SIGN_KEY.equals(pName)) continue;Object pValue = request.getParameter(pName);params.put(pName, pValue);}String originSign = request.getParameter(SIGN_KEY);String sign = createSign(params, accessSecret);return sign.equals(originSign);}
  1. 完整代码:

这里通过拦截器来实现接口拦截,可自行替换

package com.mlcs.mop.common.web.interceptor;import com.mlcs.core.conf.ZKClient;
import com.mlcs.mop.common.web.util.MD5Util;
import com.mlcs.mop.common.web.util.WebUtils;
import org.apache.zookeeper.KeeperException;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;/*** Author: Kelin* Date:  2018/5/16* Description:*/
@SuppressWarnings("SuspiciousMethodCalls")
public class SimpleApiSignInterceptor extends HandlerInterceptorAdapter {// 签名超时时长,默认时间为5分钟,msprivate static final int SIGN_EXPIRED_TIME = 5 * 60 * 1000;private static final String API_SIGN_KEY_CONFIG_PATH = "/mop/common/system/api_sign_key_mapping.properties";private static final String SIGN_KEY = "sign";private static final String TIMESTAMP_KEY = "timestamp";private static final String ACCESS_KEY = "accessKey";private static final String ACCESS_SECRET = "accessSecret";private static Map<String, String> map = new ConcurrentHashMap<String, String>();static {// 从zk加载key映射到内存里面try {String data = ZKClient.get().getStringData(API_SIGN_KEY_CONFIG_PATH);Properties properties = new Properties();properties.load(new StringReader(data));for (Object key : properties.keySet()) {map.put(String.valueOf(key), properties.getProperty(String.valueOf(key)));}} catch (KeeperException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Map<String, Object> result = new HashMap<String, Object>();String timestamp = request.getParameter(TIMESTAMP_KEY);String accessKey = request.getParameter(ACCESS_KEY);String accessSecret = map.get(accessKey);if (!org.apache.commons.lang.StringUtils.isNumeric(timestamp)) {result.put("code", 1000);result.put("msg", "请求时间戳不合法");WebUtils.writeJsonByObj(result, response, request);return false;}// 检查KEY是否合理if (StringUtils.isEmpty(accessKey) || StringUtils.isEmpty(accessSecret)) {result.put("code", 1001);result.put("msg", "加密KEY不合法");WebUtils.writeJsonByObj(result, response, request);return false;}Long ts = Long.valueOf(timestamp);// 禁止超时签名if (System.currentTimeMillis() - ts > SIGN_EXPIRED_TIME) {result.put("code", 1002);result.put("msg", "请求超时");WebUtils.writeJsonByObj(result, response, request);return false;}if (!verificationSign(request, accessKey, accessSecret)) {result.put("code", 1003);result.put("msg", "签名错误");WebUtils.writeJsonByObj(result, response, request);return false;}return true;}private boolean verificationSign(HttpServletRequest request, String accessKey, String accessSecret) throws UnsupportedEncodingException {Enumeration<?> pNames = request.getParameterNames();Map<String, Object> params = new HashMap<String, Object>();while (pNames.hasMoreElements()) {String pName = (String) pNames.nextElement();if (SIGN_KEY.equals(pName)) continue;Object pValue = request.getParameter(pName);params.put(pName, pValue);}String originSign = request.getParameter(SIGN_KEY);String sign = createSign(params, accessSecret);return sign.equals(originSign);}private String createSign(Map<String, Object> params, String accessSecret) throws UnsupportedEncodingException {Set<String> keysSet = params.keySet();Object[] keys = keysSet.toArray();Arrays.sort(keys);StringBuilder temp = new StringBuilder();boolean first = true;for (Object key : keys) {if (first) {first = false;} else {temp.append("&");}temp.append(key).append("=");Object value = params.get(key);String valueString = "";if (null != value) {valueString = String.valueOf(value);}temp.append(valueString);}temp.append("&").append(ACCESS_SECRET).append("=").append(accessSecret);return MD5Util.MD52(temp.toString()).toUpperCase();}
}

原文:https://my.oschina.net/KelinM/blog/1925209

简单API接口签名验证设计相关推荐

  1. 简单API接口签名验证

    前言 后端在写对外的API接口时,一般会对参数进行签名来保证接口的安全性,在设计签名算法的时候,主要考虑的是这几个问题: 1. 请求的来源是否合法 2. 请求参数是否被篡改 3. 请求的唯一性 我们的 ...

  2. java验证签名_简单API接口签名验证

    前言 后端在写对外的API接口时,一般会对参数进行签名来保证接口的安全性,在设计签名算法的时候,主要考虑的是这几个问题: 1. 请求的来源是否合法 2. 请求参数是否被篡改 3. 请求的唯一性 我们的 ...

  3. 开放API接口签名验证,让你的接口从此不再裸奔

    点击上方蓝色"终端研发部",选择"设为星标" 学最好的别人,做最好的我们 接口安全问题 请求身份是否合法? 请求参数是否被篡改? 请求是否唯一? AccessK ...

  4. 拒绝接口裸奔!开放API接口签名验证!

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:r6d.cn/kChH 接口安全问题 请求身份是否合 ...

  5. 公司接口裸奔10年了,有必要用API接口签名验证吗?

    点击上方"阿拉奇学Java",选择"置顶或者星标" 每天早晨00点00分,与你相约! 往日回顾:微信支付的架构到底有多牛? 接口安全问题 请求身份是否合法? 请 ...

  6. 【转】开放api接口签名验证

    不要急,源代码分享在最底部,先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候, ...

  7. java接口安全怎么处理_Restful API 接口安全性设计

    1.API接口设计规范 2.安全性设计 a.白名单限制 仅接受特定系统的请求响应,调用方的IP地址需要在本系统中报备,否则无法调用 b.合法身份合法性验证 Basic Authentication : ...

  8. 开放api接口签名验证

    不要急,源代码分享在最底部,先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候, ...

  9. api接口签名验证(MD5)

    转自  https://www.cnblogs.com/jinshui/p/6145167.html 你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我 ...

最新文章

  1. hbuilderx 2.9.8 公用css样式_浅谈关于vue中scss公用的解决方案
  2. java 国际化例子_JavaSE 国际化 简单例子
  3. 蔡先生论道大数据十九:王羲之与大数据
  4. 小雷郑重承诺:在2017年之前,对大学毕业4年以来的所有努力和探索,做一个全面客观的总结,技术研究、工作创业、投资理财、朋友感情等...
  5. linux卸载数据库后还在吗,oracle停止数据库后linux完全卸载oracle的详细步骤
  6. CodeForces - 1332D Walk on Matrix(构造)
  7. java多线程传值覆盖_Java 多线程传值的四种方法
  8. linux内核开发_Linux 内核的代码仓库管理与开发流程简介
  9. 初学Hibernate
  10. java中同步组件_Java并发编程(自定义同步组件)
  11. SpringSecurity实现记住我功能
  12. 阴谋还是骗局?美国最牛家族的“董事长”,跑到中国开了家假银行.....
  13. hive并行执行job
  14. 使用WinPcap编程
  15. Qt中实现鼠标作图并且控制每一笔粗细和颜色的方法
  16. 百度地图坐标和高德地图坐标转换
  17. 《中国程序化广告技术生态图》2015年三月号更新发布
  18. php中getopt函数的使用
  19. Vuex是干什么的?以及核心概念
  20. CultureInfo 类中需要的【区域性名称】查询

热门文章

  1. u盘格式化后数据能恢复吗?当然可以,5步恢复U盘数据
  2. 黎明觉醒服务器维护什么时候恢复,黎明觉醒健康值怎么恢复 健康值恢复方法详解...
  3. 阿里云服务器如何使用admin账户登录
  4. 【FLIR工业相机】一、环境配置:win10+VS2017+qt5+spinnaker+opencv+python
  5. linux设备驱动开发之udev用户空间设备管理
  6. 镜头扭曲Luminary 4K视频素材
  7. 视觉SLAM十四讲(高翔第二版)
  8. 申请腾讯地图用户Key流程
  9. 同一局域网主机连接另一台电脑的虚拟机
  10. 优盘数据恢复如何操作?恢复U盘数据的三个简单方法