API开放平台——API 签名认证学习笔记

API 签名认证

我们现在的接口是暴露在外的,不能随便让其他人不停刷接口,导致服务崩溃。因此,我们需要保证让用户无法无限调用接口。保证安全性。但是我们的接口是开放的,用户未登录也可以去调用接口,因此无法直接通过 Cookie 下的 Session 去取出用户来做限制。因此要用别的方法。

接口认证介绍:

参考:Java API接口签名认证_java设计接口需要签名认证-CSDN博客

什么是接口签名认证

比如,申请了微信公众号或者小程序,公众号的基本信息中就会包含AppId和AppSecret两个数据。这两个数据需要我们用户进行保存,尤其是AppSecret更不能暴露在外,以保证安全。当我们需要请求微信进行授权登录的时候,我们需要根据微信的规则拼接请求链接,其中请求链接中会包含AppId,AppSecret等的一些信息,通过按规则拼接好的链接则可以成功请求微信,否则会请求失败。
而我们要做的就是根据微信的这个原理,实现自己程序的API接口签名验证。

本质:

  1. 签发签名
  2. 使用签名(校验签名)

为什么需要签名认证?

  1. 保证安全性,不能随便一个人调用

详细地说就是:

提供了一个能够让用户不用登录账号就能调用接口的方法,用户只需要提供自己的 accessKey 和 secretKey 即可自由地在自己的代码中调用平台的接口。

怎么实现API签名认证?

accessKey:调用的标识,可以发送给后端。

SecretKey:密钥,注意该参数是不传递给后端的,它需要被加密后才会发送。

我们要求用户不管是否登录,每次调用接口都要携带这两个数据。 ak,sk (即 accessKey ,SecretKey )是无状态的,和用户名密码的有状态是区别开的。

可以自己写代码生成 ak 和 sk 比如 MD5 加密

千万不能把密钥在服务器之间传递,有可能被拦截

常见加密方式: 对称加密、非对称加密、MD5 签名(不可解密)

用户参数 + 密钥 => 签名算法 => 不可解密的值

客户端用上述的方式生成了不可解密的值,发送给后端。

后端利用一模一样的算法和参数去生成签名,只要和用户传的值一致。就表示一致。

请求重放(重放攻击)

用户向网站发送的一个请求,可以被拦截之后,被修改一些参数之后再次发送。

参考文章:面试官:啥是请求重放呀? - why技术 - 博客园 (cnblogs.com)

怎么防重放?

加 nonce 随机数,后端只会认识一次这个随机数,一旦使用过,就无法再被使用。

问题:服务端要保存用过的随机数,如果对于高并发量,高访问量的请求,需要存储大量的保存过的随机数。

加 timestamp 时间戳,每个请求都要发送一个时间戳,后端会限制只有在一定时间下的请求才能通过,校验时间戳是否过期。对于已经过期的随机数,可以将其清除,缓解存储空间。

综上,一个标准的签名认证算法,需要至少 5 个参数:

  1. accessKey
  2. SecretKey
  3. 用户请求参数
  4. sign (标志)
  5. 随机数 nonce
  6. 时间戳 timestamp

注意密码一定不要在服务器之间传输!!!

API 签名认证是一个很灵活的设计,具体要有哪些参数、参数名如何一定要根据场景来。(比如 userId 、appId、version、固定值等)

一个简单的 API 签名认证实现:

  1. getHeaderMap 方法,将需要签名的参数放入到一个 HashMap 中。
public Map<String,String> getHeaderMap(String body){
    HashMap<String, String> hashMap = new HashMap<>();
    hashMap.put("accessKey",accessKey);
    hashMap.put("body",body);
    hashMap.put("nonce", RandomUtil.randomNumbers(4));
    hashMap.put("timestamp",String.valueOf(System.currentTimeMillis()/1000));
    hashMap.put("sign", signUtils.genSign(body,secretKey));
    return hashMap;
}

  1. 后端内部向接口发送请求。同时在请求头中添加调用了 getHeaderMap 方法所生成的 json。
// 使用POST方法向服务器发送User对象,并获取服务器返回的结果
public String getUserNameByPost(User user) {
    // 将User对象转换为JSON字符串
    String json = JSONUtil.toJsonStr(user);
    // 使用HttpRequest工具发起POST请求,并获取服务器的响应
    HttpResponse httpResponse = HttpRequest.post("http://localhost:8090/api/name/user/")
            .body(json)
            //将整个 json 作为请求头,去混入到 API 签名认证中。
            .addHeaders(getHeaderMap(json))
            .execute(); // 执行请求
    // 打印服务器返回的状态码
    System.out.println(httpResponse.getStatus());
    // 获取服务器返回的结果
    String result = httpResponse.body();
    // 打印服务器返回的结果
    System.out.println(result);
    // 返回服务器返回的结果
    return result;
}

在请求服务端需要预先将请求头中的数据取出,校验请求头数据,若请求头的校验通过,就使用相同的加密算法得出相应的密钥,如果密钥匹配,则执行相应的业务。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值