一.背景

1.与前端对接的API接口,如果被第三方抓包并进行恶意篡改参数,可能会导致数据泄露,甚至会被篡改数据

2.与第三方公司的接口对接,第三方如果得到你的接口文档,但是接口确没安全校验,是十分不安全的

我主要围绕时间戳,token,签名三个部分来保证API接口的安全性

二.请求过程

1.用户成功登陆站点后,服务器会返回一个token,用户的任何操作都必须带了这个参数,可以将这个参数直接放到header里。

2.客户端用需要发送的参数和token生成一个签名sign,作为参数一起发送给服务端,服务端在用同样的方法生成sign进行检查是否被篡改。

3.但这依然存在问题,可能会被进行恶意无限制访问,这时我们需要引入一个时间戳参数,如果超时即是无效的。

4.服务端需要对token,签名,时间戳进行验证,只有token有效,时间戳未超时,签名有效才能被放行。

概念:

(1)开放接口

没有进行任何限制,简单粗暴的访问方式,这样的接口方式一般在开放的应用平台,查天气,查快递,只要你输入正确对应的参数调用,即可获取到自己需要的信息,我们可以任意修改参数值。

(2)Token认证获取

用户登录成功后,会获取一个ticket值,接下去任何接口的访问都需要这个参数。我们把它放置在redis内,有效期为10分钟,在ticket即将超时,无感知续命。延长使用时间,如果用户在一段时间内没进行任何操作,就需要重新登录系统。

(3)Sign签名

把所有的参数拼接一起,在加入系统秘钥,进行MD5计算生成一个sign签名,防止参数被人恶意篡改,后台按同样的方法生成秘钥,进行签名对比。

(4)重复访问

引入一个时间戳参数,保证接口仅在一分钟内有效,需要和客户端时间保持一致。

(5)拦截器

每次请求都带有这三个参数,我们都需要进行验证,只有在三个参数都满足我们的要求,才允许数据返回或被操作。

三.具体代码实现

1.编写获取tiket的接口

    /**
* 获取tiket
* @param receiveRequest
* @return
*/
@ResponseBody
@RequestMapping(value = "/gettiket",method = RequestMethod.POST)
public String gettiket(@RequestBody String data){
String result = "";
String msg = "";
try{
log.info("gettiket,入参为==="+data); JdbcTemplate jdbcTemplate = new JdbcTemplate();
String userTocken = UUID.randomUUID().toString();
//cache.put(userTocken, userMap);//数据库方式或者redis方式,这里用数据库方式
String insert_user_token_sql = "insert into user_token(pk_user_token,userid,user_token) VALUES (?,?,?)";
long pk_user_token = KeyUtils.nextId();//主键 jdbcTemplate.executeUpdate(insert_user_token_sql, new Object[]{
pk_user_token,"111",userTocken
});
result = userTocken;
msg = "{\"success\" : true,\"errorCode\" : \"200\", \"errorMsg\" : \"查询完成\", \"tiket\" :" +result + "}";
log.info("msg===="+msg);
return msg;
}catch(Exception e){
msg = "{\"success\" : true,\"errorCode\" : \"500\", \"errorMsg\" : \"查询完成\", \"data\" :" +e + "}";
return msg;
} }

2.服务端验证

主程序入口

        Map<String, String> paramMap = new HashMap<>();
String time = DateUtils.formatDate("yyyy-MM-dd HH:mm:ss.SSS");
paramMap.put("time", time);
String ticket = "056a3d29-eed3-4ee9-80aa-c03321d5302f";
paramMap.put("ticket", ticket);//userTock为我第一次请求你的单点url时传给你的userTocken
String serviceCode = "cs_demo";// 目标系统对应的密钥
String sign = null;
try {
sign = SignUtils.sing(paramMap, serviceCode, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
log.info("sign==="+sign);
CheckPerService checkPerService= new CheckPerService();
Boolean istrue = checkPerService.TicketSignAndTime( ticket, sign, time, serviceCode);
log.info("istrue==="+istrue);

工具类SignUtils

package tcc.test.utill;

import org.apache.log4j.Logger;
import org.springframework.util.DigestUtils;
import java.io.UnsupportedEncodingException;
import java.util.*; public class SignUtils {
private static final Logger a = Logger.getRootLogger(); public SignUtils() {
} public static String getContent(Map params) {
List keys = new ArrayList(params.keySet());
Collections.sort(keys);
String prestr = "";
boolean first = true; for(int i = 0; i < keys.size(); ++i) {
String key = (String)keys.get(i);
if (!"sign".equals(key) && !"_r".equals(key) && !"_result_type".equals(key) && !"_".equals(key)) {
String value = String.valueOf(params.get(key));
if (value != null && value.trim().length() != 0) {
if (first) {
prestr = prestr + key + "=" + value;
first = false;
} else {
prestr = prestr + "&" + key + "=" + value;
}
}
}
} a.info("加密字符串:" + prestr);
return prestr;
} public static String sing(Map Params, String key, String charset) throws UnsupportedEncodingException {
String signStr = null;
signStr = DigestUtils.md5DigestAsHex((getContent(Params) + key).getBytes(charset));
return signStr;
} public static void main(String[] args) throws Exception {
Map paramMap = new HashMap<String,String>();
paramMap.put("name","tcc");
paramMap.put("age","24");
String serviceCode = "siruinet";
String sing = SignUtils.sing(paramMap, serviceCode, "UTF-8");
System.out.println(sing); }
}

权限校验工具类

package tcc.test.utill;

import com.alibaba.druid.util.StringUtils;
import com.util.FieldList;
import jos.engine.core.jdbc.JdbcTemplate;
import jos.engine.des.util.DesEncryptUtils;
import org.apache.log4j.Logger; import java.util.HashMap;
import java.util.Map; /**
* Copyright (C) @2022
*
* @author: tcc
* @version: 1.0
* @date: 2022/1/31
* @time: 2:08
* @description:
*/
public class CheckPerService{
private static final Logger log = Logger.getRootLogger(); /*
接口权限校验方法1
ticket:票据
sign:签名
time:时间戳
serviceCode:服务编码*/
public static boolean TicketSignAndTime(String ticket, String sign, String time, String serviceCode){
time = time;
ticket = ticket;
sign = sign;
Map<String, String> paramMap = new HashMap<>();
paramMap.put("time", time);
paramMap.put("ticket", ticket);//ticket为第一次调用获取ticket接口的数据
serviceCode = serviceCode;// 目标系统对应的密钥
String qm = DesEncryptUtils.sing(paramMap, serviceCode, "UTF-8");
log.info("qm==="+qm);
if (!StringUtils.equals(sign, qm)) { //密钥校验错误
log.info("签名不正确");
return false;
}
log.info("签名正确");
JdbcTemplate jdbcTemplate = new JdbcTemplate();
String qr_user_token_sql = "select count(1) as count from user_token where user_token = ?";//后期改成redis
FieldList file_token = jdbcTemplate.queryField(qr_user_token_sql, new Object[]{ ticket });
int count = Integer.parseInt(file_token.get("count"));
if(count<1){
return false;
}
return true;
} /*
接口权限校验方法2
name:用户名
pwd:密码
*/
public static boolean UnmAndPwd(String name,String pwd){
JdbcTemplate jdbcTemplate = new JdbcTemplate("mzdb");
String qr_user_token_sql = "select count(1) as count from bd_user where USERNAME = ? and USERPASS = ?";//后期改成redis
FieldList file_token = jdbcTemplate.queryField(qr_user_token_sql, new Object[]{ name,pwd });
int count = Integer.parseInt(file_token.get("count"));
if(count<1){
return false;
}
return true; } }

API 接口的安全设计验证:ticket,签名,时间戳的更多相关文章

  1. API接口的安全设计验证—ticket,签名,时间戳

    概述 与前端对接的API接口,如果被第三方抓包并进行恶意篡改参数,可能会导致数据泄露,甚至会被篡改数据,我主要围绕时间戳,token,签名三个部分来保证API接口的安全性 1.用户成功登陆站点后,服务 ...

  2. HTTP API接口安全设计

    HTTP API接口安全设计 API接口调用方式 HTTP + 请求签名机制   HTTP + 参数签名机制 HTTPS + 访问令牌机制 有没有更好的方案? OAuth授权机制 OAuth2.0服务 ...

  3. API接口签名校验

    在开发app中,我们经常要为app提供接口.但是为了保证数据的安全,我们通常会对接口的参数进行加密. 1.不验证的接口api api接口请求,"http://www.xx.com/getUs ...

  4. 对飞猪H5端API接口sign签名逆向实验

    免责声明 本文章所提到的技术仅用于学习用途,禁止使用本文章的任何技术进行发起网络攻击.非法利用等网络犯罪行为,一切信息禁止用于任何非法用途.若读者利用文章所提到的技术实施违法犯罪行为,其责任一概由读者 ...

  5. SpringBoot接口 - API接口有哪些不安全的因素?如何对接口进行签名?

    在以SpringBoot开发后台API接口时,会存在哪些接口不安全的因素呢?通常如何去解决的呢?本文主要介绍API接口有不安全的因素以及常见的保证接口安全的方式,重点实践如何对接口进行签名.@pdai ...

  6. Winform混合式开发框架访问Web API接口的处理

    在我的混合式开发框架里面,集成了WebAPI的访问,这种访问方式不仅可以实现简便的数据交换,而且可以在多种平台上进行接入,如Winform程序.Web网站.移动端APP等多种接入方式,Web API的 ...

  7. 开放api接口签名验证

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

  8. 总结的一些微信API接口

    本文给大家介绍的是个人总结的一些微信API接口,包括微信支付.微信红包.微信卡券.微信小店等,十分的全面,有需要的小伙伴可以参考下. 1. [代码]index.php <?php include ...

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

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

随机推荐

  1. OAuth2.0的定义

    1. 什么是OAuth2.0 * 用于REST/APIs的代理授权框架(delegated authorization) * 基于令牌Token的授权,在无需暴露用户密码的情况下,使应用能获取对用户数 ...

  2. Swoole 中协程的使用注意事项及协程中的异常捕获

    协程使用注意事项 协程内部禁止使用全局变量,以免发生数据错乱: 协程使用 use 关键字引入外部变量到当前作用域禁止使用引用,以免发生数据错乱: 不能使用类静态变量 Class::$array / 全 ...

  3. Spring @Valid 和 @Validated 的区别和使用

    两者区别 @Valid @Validated 标准 标准JSR-303规范 增强JSR-303规范 包 javax.validation org.springframework.validation ...

  4. 木棒poj1011

    题目描述 乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位. 然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度. 请你设计一个程 ...

  5. RabbitMQ使用 prefetch_count优化队列的消费,使用死信队列和延迟队列实现消息的定时重试,golang版本

    RabbitMQ 的优化 channel prefetch Count 死信队列 什么是死信队列 使用场景 代码实现 延迟队列 什么是延迟队列 使用场景 实现延迟队列的方式 Queue TTL Mes ...

  6. 物理机异常断电,linux虚拟机系统磁盘mount失败,导致无法启动; kubectl 连接失败

    虚拟机 CentOS 7 挂载文件系统失败 上周五下班前没有关闭虚拟机和物理机, 今天周一开了虚拟机之后,发现操作系统启动失败. 原因跟 这篇文章描述的一模一样. 解决操作系统的文件系统挂载的问题之后 ...

  7. spring boot --- 使用 注解 读取 properties 文件 信息

    1.前言 以前使用spring MVC框架 ,读取properties 配置文件需要写一大堆东西 ,也许 那时候 不怎么使用注解 ,现在使用spring boot ,发现了非常简便的办法---使用注解 ...

  8. vue中computed的作用以及用法

    在vue中computed是计算属性,主要作用是把数据存储到内存中,减少不必要的请求,还可以利用computed给子组件的data赋值. 参考地址:https://www.jianshu.com/p/ ...

  9. Jupyter常用配置

    一  安装 pip install --upgrade jupyterthemes 二 设置主题 #查看主题列表 jt -l #设置主题并打开工具栏 jt -t chesterish -T 三 设置列 ...

  10. 《剑指offer》面试题30. 包含min函数的栈

    问题描述 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min.push 及 pop 的时间复杂度都是 O(1).   示例: MinStack minSt ...