构建API接口和用户认证的实践指南 | 青训营

608 阅读7分钟

构建API接口

目录

设计难以被误用的API

警惕采用几个相同类型参数的函数

为其默认用例设计API、

不鼓励使用nil作为参数

设计难以被误用的API

如果一个 API 很难用于简单的事情,那么 API 的每次调用都会很复杂。 当 API 的实际调用很复杂时,它就会变得不那么清晰,而且会更容易被忽视,所以我们要设计难以被无用的API。

警惕采用几个相同类型参数的函数

举个例子:一种看起来简单,但是很难去正确使用的 API 是采用两个或更多相同类型参数的 API。让我们比较两个函数:

func Max(a, b int) int
func CopyFile(to, from string) error

这两个函数有什么区别? 显然,一个返回两个数字最大的那个,另一个是复制文件。

Max(8, 10) // 10
Max(10, 8) // 10

Max 是可交换的; 参数的顺序无关紧要。 无论是 8 比 10 还是 10 比 8,最大的都是 10。

但是,却不适用于 CopyFile。

CopyFile("/tmp/backup", "presentation.md")
CopyFile("presentation.md", "/tmp/backup")

这些声明中哪一个备份了 presentation.md,哪一个用上周的版本覆盖了 presentation.md? 没有文档,我们无法分辨。 如果没有查阅文档,代码审查员也无法知道我们是否写对了顺序。

一种可能的解决方案是引入一个 helper 类型,它会负责如何正确地调用 CopyFile。

package main

type Source string

func (src Source) CopyTo(dest string) error {
 return CopyFile(dest, string(src))
}

func main() {
 var from Source = "presentation.md"
 from.CopyTo("/tmp/backup")
}

通过这种方式,CopyFile 总是能被正确调用,进一步降低了误用的可能性。

贴士: 具有多个相同类型参数的 API 难以正确使用。

总结:使用 helper 类型帮助明确函数的参数用途,来避免多个同类型参数造成的混淆。

为其默认用例设计API

我们应该为常见用例设计 API。 另一方面, API 不应要求调用者提供他们不在乎的参数。

不鼓励使用nil作为参数

①: 不要在同一个函数签名中混合使用可为 nil 和不能为 nil 的参数。

②: 认真考虑 helper 函数会节省不少时间。 清晰要比简洁好。

③: 避免公共 API 使用测试参数 避免在公开的 API 上使用仅在测试范围上不同的值。 相反,使用 Public wrappers 隐藏这些参数,使用辅助方式来设置测试范围中的属性。

用户认证

目录

认证机制

HTTP基本认证

token认证

API key认证

认证机制

在开始编写代码之前,我们先来讨论一下如何对API的请求进行身份验证,并找出请求来自于哪个用户。

选择一种高级的API身份验证方法可能很困难——有许多不同的选项,而且并不能立即清楚哪种方法适合您的项目。因此,在本节中,我们将从宏观层面讨论一些最常见的方法,讨论它们的优缺点,并在结束时介绍哪些情况适合使用哪种方法。

具体来说,我们将对比以下3种认证方式:

①HTTP基本认证 ②token认证 ③API key认证

HTTP基本认证

确定谁向API服务发出请求的最简单方法可能是使用HTTP基本身份验证。使用此方法,客户端包含一个Authorization请求头,每个请求都包含它们的凭证。凭证采用用户名:密码和base-64编码的格式。因此,例如,要验证alice@example.com:pa55word,客户端将发送以下报头:

Authorization: Basic YWxpY2VAZXhhbXBsZS5jb206cGE1NXdvcmQ=

服务端可以使用Go的Request.BasicAuth()方法从请求头中提取凭证信息,在继续处理请求之前,验证凭证信息是否正确。HTTP基本身份验证的一大优点是对客户端来说非常简单。用户只需要为每个请求发送相同的头信息即可——大多数编程语言、web浏览器以及curl和wget等工具都支持HTTP基本身份验证。

当我们的API没有用户管理,但想要一种快速、简单的方式来限制对API的访问或保护它不被窥探时,这种方法通常很有用。

对于有用户账号的APIs服务,尤其是使用密码哈希处理的,HTTP基本认证不是很合适。将客户端提供的密码和密码的哈希值做对比是一个代价比较高的操作,而使用HTTP基本认证需要每个请求都做对比处理。这将为API服务器带来大量额外的工作,并增加响应延迟。但即便如此,如果API的流量非常低,响应速度对我们来说并不重要,那么基本身份验证仍然是一个不错的选择。

token认证

token认证的工作原理:

1、客户端发送请求到API服务时包含认证信息(一般是用户名或邮箱以及密码)。

2、API服务验证认证信息是否正确,并生成代表用户身份的bearer token(不记名令牌)返回给客户端。token在一段时间后过期,在此之后,用户将需要重新提交他们的凭证以获得一个新的token。

3、对于后续的API请求,客户端将拿到的token放在Authorization请求头中,如下所示:

Authorization: Bearer <token>

4、当API服务接收到这个请求时,它检查token是否过期,并检token值以确定用户是谁。

对API服务的用户密码做了哈希处理(类似本系列文章前面所述),这种方法比基本认证更合适,因为耗时的密码校验只需要定期执行即可-第一次创建token和token过期才需要校验密码。

这种方法的缺点是对客户端来说管理token可能比较复杂-客户端需要实现必要的token缓存逻辑,监控和管理token的过期,定期生成新的token。

API key认证

API-key认证的思想是用户有一个和账号关联的不过期“key“。该key是一个高墒安全的随机字符串以及key的哈希值(SHA256或SHA512)需要和用户ID一起存在数据库中。

用户在每个API请求头中都带上他们的key,如下所示:

Authorization: Key <key>

API服务接收到请求后,可以快速生成key的哈希值,根据哈希值查询数据库中对应的用户ID。从概念上讲,这与有状态token方法没有什么不同——主要的区别是key是永久的,而不是临时token。

一方面客户端可以使用同一个key来发起每个请求,不需要写代码管理token及其过期时间。另一方面,用户现在有两个长期存在的安全信息需要管理,这两个安全信息可能会危及他们的帐户,分别是:密码和API Key。

支持API keys对服务端来说也增加了额外的复杂性,需要一种方式来生成API key如果丢失了或者泄漏的情况,而且我们可能希望一个用户有多个API key,用户可以根据不同用途使用不同key。

同样重要的是,API key本身应该只通过安全通道与用户通信,并且应该像对待用户密码一样对待它们。

总结

对于选择哪种身份验证方法最适合的API,很难给出全面的方法。就像编程中的大多数事情一样,不同的工具适用于不同的工作。

但简单粗略的经验法则是:

①如果你的API服务没有用户账号,及其密码带哈希值的话,HTTP基础认证可能比较适合,而且经常被忽视。

②如果您需要委托身份验证,比如当您的API有一个微服务架构,有不同的服务来执行身份验证和执行其他任务时,那么就使用无状态身份验证tokens。 否则使用API key或有状态认证tokens

总之:

有状态身份验证token非常适合API作为网站或单页应用程序的后端,因为当用户登录时,可以很自然地用它们交换用户凭证。

相反,API keys使用于更通用的API服务,因为开发人员在应用程序和脚本中可永久使用,而且更简单。