美文网首页
【Go Web开发】用户认证

【Go Web开发】用户认证

作者: Go语言由浅入深 | 来源:发表于2022-03-22 09:28 被阅读0次

    接下来的几篇文章,我们将看看如何对API服务的请求进行认证,以便我们准确地知道一个请求来自于哪个用户。

    记住:认证是关于确认用户是谁,而授权是检查用户是否有权做某些事情。

    我们将:

    • 介绍可以使用的API身份验证方法,并讨论它们的优缺点。
    • 实现一种基于token的有状态认证模式,允许客户端交换他们的用户凭证,以获得一个有时间限制的身份验证token来识别用户。

    认证机制

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

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

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

    • HTTP基本认证
    • 有状态的token认证
    • 无状态的token认证
    • API key认证
    • OAuth2.0/OpenID连接

    提示:关于我们在本节中描述的所有身份验证方法,前提是你的API只通过HTTPS与客户端通信。

    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。

    我们可以将token认证进一步分成两类:有状态的和无状态的认证。它们的优点和缺点是非常不同的,所以让我们分别讨论。

    有状态token认证

    在有状态token认证方法中,token的值是一个高熵(随机性高)密码安全的随机字符串。这个token或其哈希值以及用户ID和令牌的失效时间,都是存储在服务端的数据库中。

    当客户端在请求当中携带token,API服务查询数据库中token检查是否过期,并检索对应的用户ID来确认请求来自哪个用户。这种认证方式的优点是服务端控制tokens,通过从数据库中删除token或将它们标记为过期,很直接地实现一个用户对应一个token。

    从理论上讲,它也很简单和健壮——通过“不可猜测的”token来提供安全性,这就是为什么使用一个高熵加密安全的随机token值很重要。

    那缺点是什么呢?

    除了管理token给客户端带来复杂性之外,对于这种方法,我们很难找到太多可以批评的地方。也许它需要数据库查询算一个缺点——但在大多数情况下,都需要进行数据库查找,以检查用户的激活状态或检索有关他们的额外信息。

    无状态token认证

    与有状态认证不同,无状态token将用户ID和过期时间都编码到token中。对token进行加密签名以防止篡改,并且(在某些情况下)对token进行加密以防止读取其内容。

    有几种创建无状态token的技术。将信息加密到一个JWT(JSON Web Token)中可能是最常见的方法,但PASETOBrancanacl/secretbox也是不错的选择。尽管这几种技术实现细节不同,在认证方面的主要优点和缺点是相似的。

    使用无状态token进行身份验证的主要优点是,对token进行编码和解码的工作可以在内存中完成,标识用户所需的所有信息都包含在令牌本身中。不需要执行数据库查询来找出请求来自哪个用户。

    无状态token的主要缺点是,一旦它们被发行,就不容易被撤销。

    在紧急情况下,您可以通过更改用于token签名的密码来有效地撤销所有令牌(迫使所有用户重新认证),或者另一种解决方法是在数据库中维护一个被撤销token列表(尽管这违背token的“无状态”特性)。

    提示:通常您应该避免在无状态token中存储额外的信息,例如用户的激活状态或权限,并将其作为授权检查的基础。在token的生命周期内,编码到其中的信息可能会过时,并且与系统中的实际数据不同步——依赖旧数据进行授权检查很容易导致用户的意外行为和各种安全问题。

    最后,尤其是JWT,它们高度可配置意味着可能容易出错。JWT库中的关键漏洞JWT安全最佳实践两篇文章很好的介绍了你需要注意的细节。由于这些缺点,无状态token(特别是JWT)通常不是大多数API应用程序中管理身份验证的最佳选择。

    但是它们在需要委托身份验证的场景中非常有用——创建身份验证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本身应该只通过安全通道与用户通信,并且应该像对待用户密码一样对待它们。

    OAuth2.0/OpenID Connect

    另一中方法是使用OAuth2.0来验证。使用这种方法,用户的信息是保存在第三方身份认证提供者那里,例如Google或Facebook而不是自己的服务中。

    关于OAuth2.0首先要提到的是它不是一种认证协议,你不能用它来认证用户。关于OAth2.0 oauth.net有相关的文章介绍我强力推荐。

    如果你想使用第三方服务实现认证,应该使用OpenID Connect(它是基于OAuth2.0构建的)。

    这里有一个关于OpenID Connect的全面描述,但站在一个宏观层面,它是这样工作的:

    1、当你要认证一个请求时,您将用户重定向到由身份提供者托管的“身份验证和同意”表单。

    2、如果用户同意,那么身份提供者将向API发送授权代码。

    3、然后,您的API将授权码发送到认证提供者提供的另一个接口。它们验证授权代码,如果授权码有效,它们将向您发送包含ID token的JSON响应。

    4、ID token本身是JWT,你需要校验、解码获取用户的实际信息:包括用户email地址、用户名,出生日期,时区等。

    5、你知道用户是谁,就可以实现一个有状态或无状态token认证,不需要每个请求都走上面的流程。

    和其他认证方法一样,使用OpenID Connect也有利有弊。最大的优点就是你不需要存储用户信息或密码。最大缺点就是实现复杂,尽管有一些辅助工具例如coreos/go-oidc简化了复杂性并提供了简单的OpenID connect功能接口。

    还需要指出的是使用OpenID connect需要用户有一个三方服务账号,而“认证和同意”步骤需要通过浏览器进行人机交互——如果你的API是一个网站的后端,这可能是没问题,但如果它是一个“独立的”API与其他计算机程序作为客户端,就不理想了。

    该使用哪种认证方法

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

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

    • 如果你的API服务没有用户账号,及其密码带哈希值的话,HTTP基础认证可能比较适合,而且经常被忽视。
    • 如果你不想存储用户密码,用户都有第三方认证服务账号的话,并且三方服务支持OpenID connect,API服务是一个网站,使用OpenID Connect。
    • 如果您需要委托身份验证,比如当您的API有一个微服务架构,有不同的服务来执行身份验证和执行其他任务时,那么就使用无状态身份验证tokens。
    • 否则使用API key或有状态认证tokens,总之:
      • 有状态身份验证token非常适合API作为网站或单页应用程序的后端,因为当用户登录时,可以很自然地用它们交换用户凭证。
      • 相反,API keys使用于更通用的API服务,因为开发人员在应用程序和脚本中可永久使用,而且更简单。

    在后面的文章中,我们将使用有状态认证token方式。在我们前面的编码中,作为激活token工作的一部分,我们已经为token构建了许多必要的逻辑。

    注意:尽管我不推荐JWT,除非你需要某种代理认证,我知道它们在开发者社区中有很多分享,而且经常被更广泛地使用。因此,在后面的文章中会介绍如何在Greenlight API中使用JWT实现无状态认证token。

    相关文章

      网友评论

          本文标题:【Go Web开发】用户认证

          本文链接:https://www.haomeiwen.com/subject/xngtjrtx.html