一.背景

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. Python基础案例练习:制作学生信息管理系统

    一.前言 学生信息管理系统,相信大家或多或少都有做过 最近看很多学生作业都是制作一个学生信息管理系统 于是,今天带大家做一个简单的学生信息管理系统 二.开发环境: 我用到的开发环境 Python 3. ...

  2. linux修改默认的SSH远程端口22

    1.编辑sshd_config文件 [root@localhost ~]#  vim /etc/ssh/sshd_config 搜索 #Port 22行,删除开头的 # 字符,然后将其替换为要使用的端 ...

  3. 去掉所有包含this或is的行

    题目描述 写一个 bash脚本以实现一个需求,去掉输入中含有this的语句,把不含this的语句输出 示例: 假设输入如下: that is your bag is this your bag? to ...

  4. 什么是css Modules

    具体请参考阮一峰老师的博客(http://www.ruanyifeng.com/blog/2016/06/css_modules.html)

  5. Linux上天之路(五)之Linux基本命令

    1. Linux命令格式 命令 命令选项 学会看语法: {必选项}[可选项] 举例 ls -a /tmp 等价 ls –all /tmp ls 命令 -a 命令选项 简写使用- 全写-- /tmp 参 ...

  6. angularJS中$digest already in progress报错解决方法

    看到一个前端群里有人问,就查了下解决"$digest already in progress"最好的方式,就是不要使用$scope.$apply()或者$scope.$digest ...

  7. 大厂面试来了,欢聚时代四年多经验的Java面经

    前言(也就是废话) 今年年底,额,不对,应该说是去年了,我开始进行了一个多月的面试之旅. 面试的公司并不多,但从体量上来看,基本算是一二三线的大厂都囊括了,其中还包括BAT,当然,最后我也是顺利的拿到 ...

  8. java原码、反码、补码、位运算

    1.对于有符号的数(java中的数都是有符号的) 二进制的最高位是符号位:0表示正数,1表示负数 正数的原码,反码,补码都一样 负数的反码=它的原码符号位不变,其它位取反 负数的补码=它的反码+1 0 ...

  9. vue项目再HBuilder打包成app后,有ui模块未添加的弹窗

    直接在打包后的mainifst.json的文件夹中加入标注部分,我是这样解决了的

  10. 树形dp空间优化(dfn)

    树形dp空间优化 介绍 有时题目会告诉我们n叉树的最大层数,或者给出一个完全n叉树树,直接做树形dp会爆空间时,就可以用这个优化方法. 多数树形dp都是先dfs到子树,再合并到根上,显然当合并到根上时 ...