请思考一个重要的问题:如果我们为开发者提供了一个接口,却对调用者一无所知。假设我们的服务器只能允许100个人同时调用接口。如果有攻击者疯狂的请求这个接口,那将极其危险。一方面这可能会损害我们的安全性,另一方面也可能耗尽服务器性能,影响正常用户的使用。
因此,我们必须为接口设置保护措施。例如限制每个用户每秒只能调用10次接口,即实施请求频次的限额控制。
现在,我们设计一个方法,来确定谁在调用调用 。在开发后端时,我们会进行一些权限检查。比如:当管理员执行删除操作时,后端需要检查这个用户是否为管理员。那么,是如何获取用户信息的呢?是否直接从后端的session中获取?但问题来了,当前端调用接口的时候,一定有session吗?比如说,是前端直接发起请求,并没有登录操作,没有输入用户名和密码,那怎么去调用呢?因此,一般情况下,会采用一个叫 API签名认证 的机制。
简单的说,如果你想来我家做客,我不可能随便让任何陌生人进来。所以我会提前给你发一个类似请帖的东西,作为授权或许可证。当你来访问我的时候,你需要带上这个许可证。我可能并不认识你,但我认识你的请帖。只要你有这个请帖,我就允许你进来。
所以,API签名认证主要包括两个过程:一是签发签名,二是使用签名或校验签名。
需要两个东西:accessKey 和 secretKey
这和用户名和密码类似,不过每次调用接口都需要带上,实现 无状态的请求 。这样,即使你之前没有来过,只要这次的状态正确,你就可以调用接口。所以需要这两个东西来标识用户。
在签发的过程中,可以自己编写一个生成 accessKey 和 secretKey 的工具。一般来说,accessKey 和 secretKey 需要尽可能复杂,以防止黑客尝试破解,特别是密码,需要尽可能复杂,无规律。
⚠️注意:千万不能把秘钥直接在服务器之间传递,有可能会被拦截。
用户参数 + 秘钥 => 签名生成算法(SHA-256、SHA-3等) => 不可解密的值
比如:abc + abdedfgh => sfdasidfhssdfh
服务端用一模一样的参数和算法去生成签名,只要和用户传的一致,就表示一致。
参数5:加 nonce 随机数,只能用一次(服务端要保存用过的随机数)
每次请求时,发生一个随机数给后端。后端只接受并认可该随机数一次,一旦随机数被使用过,后端将不再接受相同的随机数。这种方式解决了请求重复的问题,因为即使对方使用之前的时间和随机数进行请求,后端会认识到该请求已经被处理过,不会再次处理。
但是,这种方法需要后端额外开发来保存已使用的随机数。并且,如果接口的并发量很大,每次请求都需要一个随机数,那么可能会面临处理百万、千万甚至上亿级别请求的情况。因此,除了使用随机数之外,还需要其他机制来定期清理已使用的随机数。
参数6:加 timestamp 时间戳,检验时间戳是否过期。
每个请求在发生时携带一个时间戳,并且后端会验证该时间戳是否在指定的时间范围内。例如不超过10分钟或5分钟。这可以防止对方使用昨天的请求在今天进行重放。
通过这种方式,我们可以一定程度上控制随机数的过期时间。因为后端需要同时验证这两个参数,只要时间戳过期 或者 随机数被使用过,后端会拒绝该请求。因此,时间戳可以在一定程度上减轻后端保存随机数的负担。通常情况下,这两张方法可以相互配和使用。
因此,在标准的签名认证算法中,建议至少添加以下五个参数:accessKey、secretKey、sign、nonce、timestamp。此外,建议将用户请求的其他参数,例如接口中的name参数,也添加到签名中,已增加安全性。
API签名认证是一个很灵活的设计,具体要有哪些参数、参数名 一定要根据场景来。(比如:userId、appId、version、固定值等)
类似于HTTPS协议,签名认证的本质是确保密码不在服务器之间传输。因为任何在服务器之间传输的内容都有可能被拦截。所以,请记住密码绝不能在服务器之间传输。