概述

与前端对接的API接口,如果被第三方抓包并进行恶意篡改参数,可能会导致数据泄露,甚至会被篡改数据,我主要围绕时间戳,token,签名三个部分来保证API接口的安全性

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

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

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

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

开放接口

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

 *
* Description: 开放的接口
* @author huangweicheng
* @date 2020/12/21
*/
@RestController
@RequestMapping("/token")
public class TokenSignController { @Autowired
private TokenSignService tokenSignService; @RequestMapping(value = "openDemo",method = RequestMethod.GET)
public List<PersonEntity> openDemo(int personId){
return tokenSignService.getPersonList(personId);
}
}

Token认证获取

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

@RequestMapping(value = "login",method = RequestMethod.POST)
public JSONObject login(@NotNull String username, @NotNull String password){
return tokenSignService.login(username,password);
}

登录操作,查看是否有这个用户,用户名和密码匹配即可成功登录。

    /**
*
* Description:验证登录,ticket成功后放置缓存中,
* @param
* @author huangweicheng
* @date 2020/12/31
*/
public JSONObject login(String username,String password){
JSONObject result = new JSONObject();
PersonEntity personEntity = personDao.findByLoginName(username);
if (personEntity == null || (personEntity != null && !personEntity.getPassword().equals(password))){
result.put("success",false);
result.put("ticket","");
result.put("code","999");
result.put("message","用户名和密码不匹配");
return result;
}
if (personEntity.getLoginName().equals(username) && personEntity.getPassword().equals(password)){
String ticket = UUID.randomUUID().toString();
ticket = ticket.replace("-","");
redisTemplate.opsForValue().set(ticket,personEntity.getLoginName(),10L, TimeUnit.MINUTES);
result.put("success",true);
result.put("ticket",ticket);
result.put("code",200);
result.put("message","登录成功");
return result;
}
result.put("success",false);
result.put("ticket","");
result.put("code","1000");
result.put("message","未知异常,请重试");
return result;
}

Sign签名

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

    /**
* @param request
* @return
*/
public static Boolean checkSign(HttpServletRequest request,String sign){
Boolean flag= false;
//检查sigin是否过期
Enumeration<?> pNames = request.getParameterNames();
Map<String, String> params = new HashMap<String, String>();
while (pNames.hasMoreElements()) {
String pName = (String) pNames.nextElement();
if("sign".equals(pName)) continue;
String pValue = (String)request.getParameter(pName);
params.put(pName, pValue);
}
System.out.println("现在的sign-->>" + sign);
System.out.println("验证的sign-->>" + getSign(params,secretKeyOfWxh));
if(sign.equals(getSign(params, secretKeyOfWxh))){
flag = true;
}
return flag;
}

重复访问

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

  public static long getTimestamp(){
long timestampLong = System.currentTimeMillis(); long timestampsStr = timestampLong / 1000; return timestampsStr;
}

需要跟当前服务器时间进行对比,如果超过一分钟,就拒绝本次请求,节省服务器查询数据的消耗

拦截器

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

public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
private RedisTemplate redisTemplate; @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws IOException {
JSONObject jsonObject = new JSONObject();
String ticket = request.getParameter("ticket");
String sign = request.getParameter("sign");
String ts = request.getParameter("ts");
if (StringUtils.isEmpty(ticket) || StringUtils.isEmpty(sign) || StringUtils.isEmpty(ts)){
jsonObject.put("success",false);
jsonObject.put("message","args is isEmpty");
jsonObject.put("code","1001");
PrintWriter printWriter = response.getWriter();
printWriter.write(jsonObject.toJSONString());
return false;
}
//如果redis存在ticket就认为是合法的请求
if (redisTemplate.hasKey(ticket)){
System.out.println(redisTemplate.opsForValue().getOperations().getExpire(ticket));
String values = (String) redisTemplate.opsForValue().get(ticket);
//判断ticket是否即将过期,进行续命操作
if (redisTemplate.opsForValue().getOperations().getExpire(ticket) != -2 && redisTemplate.opsForValue().getOperations().getExpire(ticket) < 20){
redisTemplate.opsForValue().set(ticket,values,10L, TimeUnit.MINUTES);
}
System.out.println(SignUtils.getTimestamp());
//判断是否重复访问,存在重放攻击的时间窗口期
if (SignUtils.getTimestamp() - Long.valueOf(ts) > 600){
jsonObject.put("success",false);
jsonObject.put("message","Overtime to connect to server");
jsonObject.put("code","1002");
PrintWriter printWriter = response.getWriter();
printWriter.write(jsonObject.toJSONString());
return false;
}
//验证签名
if (!SignUtils.checkSign(request,sign)){
jsonObject.put("success",false);
jsonObject.put("message","sign is invalid");
jsonObject.put("code","1003");
PrintWriter printWriter = response.getWriter();
printWriter.write(jsonObject.toJSONString());
return false;
}
return true;
}else {
jsonObject.put("success",false);
jsonObject.put("message","ticket is invalid,Relogin.");
jsonObject.put("code","1004");
PrintWriter printWriter = response.getWriter();
printWriter.write(jsonObject.toJSONString());
}
return false;
}
}

访问

先登录系统,获取合法的ticket

生成一个合法的sign验证,获取测试ts,访问openDemo,即可正常访问。还可以将参数加密,将http换成https,就不一 一展开了。

demo代码 https://github.com/hwc4110/spring-demo1221

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

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

    一.背景 1.与前端对接的API接口,如果被第三方抓包并进行恶意篡改参数,可能会导致数据泄露,甚至会被篡改数据 2.与第三方公司的接口对接,第三方如果得到你的接口文档,但是接口确没安全校验,是十分不安 ...

  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. 第二十章、QTableView与QStandardItemModel开发实战:展示Excel文件内容

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.概述 在前面<第十九章.Model/View开发:QTableView的功能及属性> ...

  2. PyQt(Python+Qt)学习随笔:toolButton的toolButtonStyle属性

    toolButtonStyle属性用于确认toolButton按钮显示文字.图标的方式,其类型为枚举类型 Qt.ToolButtonStyle,有如下值: ToolButtonIconOnly(值为0 ...

  3. PHP代码审计分段讲解(7)

    17 密码md5比较绕过 <?php if($_POST[user] && $_POST[pass]) { mysql_connect(SAE_MYSQL_HOST_M . ': ...

  4. flask-mail 机制

    上课无聊,总结下学习的flask-mail 机制 flask-mail 了解 flask-mail 机制中可以用pip 安装也可以用pycharm里面直接安装. flask-mail是一个能调用smt ...

  5. 【笔记】「pj复习」深搜——简单剪枝

    深搜--简单剪枝 说在最前面: 因为马上要 NOIP2020 了,所以菜鸡开始了复习qwq. pj 组 T1 ,T2 肯定要拿到满分的,然后 T3 , T4 拿部分分, T3 拿部分分最常见的做法就是 ...

  6. XJOI contest 1592

    首先 热烈庆祝"CSP-S 2020全国开放赛前冲刺模拟训练题2"圆满结束!!! 感谢大毒瘤颗粒囊的题目.题目还是很不错的,部分分设置的不合理,各种神仙随便 AK ,蒟蒻只能爆零. ...

  7. Robot Framework+adb框架自动化测试Android设备案例⑸——L1层测试用例

    一.L1层测试用例 1.初始化.robot *** Settings *** Resource ../L2层关键字.robot *** Test Cases *** 切换EMMC模式 [Tags] A ...

  8. 数据结构—— Trie (前缀树)

    实现一个 Trie (前缀树),包含 插入, 查询, 和 查询前缀这三个操作. Trie trie = new Trie(); trie.insert("apple"); trie ...

  9. uni-app微信小程序登录授权

    微信小程序授权是非常简单和常用的功能,但为了方便,还是在此记录一下要点: 首先是需要用到一个授权按钮来触发获取用户信息授权: 关键在于 open-type 为 getUserInfo , 然后有个@g ...

  10. CIBN手机电视8.3.2永久VIP

    一款互联网电视的手机客户端.可以观看最新的电影和电视剧,还会为你推荐人气热门电影,让你不会错过每一部精彩的大片,以去除app内的所有可见广告,解锁VIP特权,无需登录直接使用! 下载地址:https: ...