java api接口签名验证失败_简单API接口签名验证
前言
后端在写对外的API接口时,一般会对参数进行签名来保证接口的安全性,在设计签名算法的时候,主要考虑的是这几个问题:
1. 请求的来源是否合法
2. 请求参数是否被篡改
3. 请求的唯一性
我们的签名加密也是主要针对这几个问题来实现
设计
基于上述的几个问题,我们来通过已下步骤来实现签名加密:
1. 通过分配给APP对应的app_key和app_secret来验证身份
2. 通过将请求的所有参数按照字母先后顺序排序后拼接再MD5加密老保证请求参数不被篡改
3. 请求里携带时间戳参数老保证请求的唯一和过期,重复的请求在指定时间(可配置)内有效
实现
签名生成:
生成当前时间戳timestamp=now
按照请求参数名的字母升序排列非空请求参数(包含accessKey) stringA="AccessKey=access&home=world&name=hello&work=java×tamp=now&nonce=random";
拼接密钥accessSecret stringSignTemp="AccessKey=access&home=world&name=hello&work=java×tamp=now&nonce=random&accessSecret=secret";
MD5并转换为大写生成签名 sign=MD5(stringSignTemp).toUpperCase();
JAVA代码如下:params是从request里面获取的所有参数map,accessSecret是加密密钥
private String createSign(Map params, String accessSecret) throws UnsupportedEncodingException {
Set 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();
}
签名校验:
参数格式校验
超时校验
验证签名
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Map result = new HashMap();
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 params = new HashMap();
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);
}
完整代码:
这里通过拦截器来实现接口拦截,可自行替换
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分钟,ms
private 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 map = new ConcurrentHashMap();
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();
}
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Map result = new HashMap();
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 params = new HashMap();
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 params, String accessSecret) throws UnsupportedEncodingException {
Set 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();
}
}
java api接口签名验证失败_简单API接口签名验证相关推荐
- api接口参数加密_解决API接口开发安全性的四种方案
如今各种API接口层出不穷,一个API的好与不好有很多方面可以考量,其中"安全性"是一个API接口最基本也是最重要的一个特点.尤其是对于充值缴费类的API接口来说,如话费充值API ...
- java监听com口_简单了解Java接口+事件监听机制
1.接口: 定义方法: public interface interName //extends interName2, interName3...可继承多个接口 在接口里只能定义常量和抽象方法. p ...
- java给第三方接口发送数据_对接第三方接口--使用post请求发送json数据
对接第三方接口–使用post请求发送json数据 实习4个多月,终于转正!终于可以安心好好上班,好好学习!第一篇播客记录下工作中的中的小知识点. 本文记录的内容如下: 1.使用HttpClient相关 ...
- android api在线文档_通过 API 远程管理 Jenkins
背景介绍 最近接到一个需求,需要对公司内部的Android性能测试平台的分支管理模块进行改造. 为了更好地说明问题,在下图中展示了一个精简的持续集成测试系统. 在该系统中,Jenkins负责定时检测代 ...
- Java编程基础10——面向对象_多态抽象类接口
1.多态的概述及其成员访问特点代码体现 A:多态(polymorphic)概述 事物存在的多种形态 B:多态前提- 1.要有继承关系 2.要有方法重写 3.要有父类引用指向子类对象. C:多态中的成员 ...
- 气象接口返回图标_天气预报查询接口
全国天气预报接口文档说明 全国天气预报接口地址 https://api.ip138.com/weather/ https://api.ip138.com/weather/ 示例1(Linux命令行下执 ...
- 无法创建接口的实例_什么是接口?
接口 接口概述 接口,是java语言中一种类型,是方法的集合,如果说 类的内部封装了成员变量,构造 方法,和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法,默认方法和静态方法 接口是对功能的 ...
- 华为路由器接口如何区分_华为路由器接口介绍
E1-F和T1-F接口 E1-F和T1-F接口是指部分(Fractional)化E1.T1接口,它们分别是CE1/PRI和CT1/PRI接口的简化版本.在E1/T1接入应用中,如果不需要划分出多个通道 ...
- java验证签名_简单API接口签名验证
前言 后端在写对外的API接口时,一般会对参数进行签名来保证接口的安全性,在设计签名算法的时候,主要考虑的是这几个问题: 1. 请求的来源是否合法 2. 请求参数是否被篡改 3. 请求的唯一性 我们的 ...
- java给朋友发微信_微信api接口,给微信好友收发消息
微信api接口,给微信好友收发消息 /** * 给微信好友发消息 * @author wechatno:tangjinjinwx * @blog http://www.wlkankan.cn */ @ ...
最新文章
- 入门Python之后还是搞不定面试、做不来项目,推荐读读这本书
- plsql tables 没有表_天长视唱练耳辅导班收费表,安徽高考音乐培训学校,你知道吗...
- selecte设置不可用使用disabled属性注意
- Dubbo——Dubbo协议整合Jackson序列化解决方案
- 外挂学习之路(11)--- 背包数据的遍历
- CYQ.Data 数据框架 V4.0 开源版本发布(源码提供下载,秋色园V2.5版本标配框架)
- html无序列表中的正方形点点,CSS 有序或者无序列表的前面的标记 list-style-type 属性的实现...
- php-fpm容易假死,实现自动重启php服务的脚本 通过后
- 我的MYSQL学习心得(九) 索引
- yagmail和keyring的安装与注册
- SQL笔试经典50题及答案解析(题目41-50)
- Spyder5 显示器校准 色彩校准
- TJA1042T/3与国产CAN芯片SIT1042T/3性能对比
- 课程列表和整合阿里云视频点播
- JQuery淡出淡入动画
- 2015-华为招聘公开测试题目-单词迷宫
- 阿里实名认证Java版(详细教程)
- 最短路径(加权有向图)
- 普通索引 唯一索引 主键索引 组合索引 全文索引
- 关于一款开源远程控制软件(gh0st)的源码分析(一)
热门文章
- 崩坏3区号+86_中国大陆国际区号是“ +86”,还是“ +086”、“ +0086”
- 创业经验谈(转自:ouravr.com)
- VMware Workstation 安装ssh服务器
- AvalonDock的基本用法
- 云端软件关闭的原因是什么?
- C++模板函数 学习记录
- ghostscript的坑
- BZOJ4399: 魔法少女LJJ
- 2021-10-12 CHIP类PCB封装的创建
- 测量法的三种测量方法计算机,圆度的测量方法有哪几种