paint-brush
但但但这是困难的经过@dbozhinovski
506 讀數
506 讀數

但但但这是困难的

经过 Darko Bozhinovski8m2024/08/19
Read on Terminal Reader

太長; 讀書

不,不是。这是一个无聊、繁琐、已解决的问题……但不要笼统地说它很难。
featured image - 但但但这是困难的
Darko Bozhinovski HackerNoon profile picture


不,不是。这是一个无聊、繁琐、已解决的问题……但不要笼统地说它很难。


我“在 PHP 中使用 MD5 来散列密码”已经好几年了。当然,这是一个糟糕的想法,即使在2012 年也是如此。但是,那时,我不记得认为身份验证“很难”。它本身是一个非常简单的考验 - 获取电子邮件或用户名,获取密码,对其进行散列(使用 MD5,正如“上帝的旨意”),如果你特别注重安全,可以对密码进行 [加盐]。将所有这些存储在某个地方,通常是在数据库中。瞧,注册完成了。


如今,这种说法已经发生了变化。“身份验证很难”似乎是一个无处不在的说法,只需点击一下 HackerNews 或 Reddit 即可。但真的是这样吗?在我看来,事实是,身份验证并不难构建 - 任何人都可以学习它(并且从事这一行的每个人都应该学习基础知识)。真正的挑战在于额外的东西:MFA、用户管理、密码重置、数百个 OAuth 提供商中的每一个以及来自不同提供商的帐户合并。这是千刀万剐。由于身份验证是一个已解决的问题,因此重新发明轮子并不是最有效的利用时间的方式。但这并不意味着“身份验证很难”这个笼统的说法是正确的,甚至接近正确。您应该进行实验,了解基础知识,然后从那里开始构建。复杂性只会随着您创建的规模(或潜在规模)而增长。


那么,身份验证到底有多难?让我们深入探讨一下。


在过去的日子里...

接着我之前关于 PHP 和 md5 的故事,构建登录功能遵循类似的步骤;获取电子邮件和密码,检查存储中是否存在该电子邮件,将密码与存储的该电子邮件的盐值一起进行哈希处理,将得到的哈希值与存储在数据库中的哈希值进行比较,如果一切正常,则通过setcookie设置 cookie(我们仍然在 PHP 领域 - 并不是说其他生态系统中的整体逻辑有太大不同)。


退出甚至更简单 - 只需使服务器上的 cookie 无效,就大功告成了。如果服务器在下一个请求中没有看到 cookie,则表示您未登录。因此,总体而言,执行经过身份验证的路由也是一件简单的事。当涉及到权限时,事情可能会变得棘手,但通常情况下,对于我必须构建的应用程序,我们只有管理员和用户。如果您需要扩展应用程序的角色数量,您可以简单地将其与用户记录一起存储或存储在权限表中。


完全披露 — 我为SuperTokens工作。然而,这篇文章是出于个人对无处不在的关于身份验证有多难的笼统说法的不满。换句话说,我并不是想“向你推销我的东西”。随你喜欢就行。


自己动手做——“现代”做法

电子邮件/密码和社交认证

为了达到我们今天的目的,我们将从头开始......我知道这很令人惊讶。我们可能同意这些组件足以构成电子邮件/密码+社交登录 PoC:


  1. 处理路线的服务器 - 注册、登录、注销......
  2. 用于保存用户记录的某种存储(内存数组也可以)
  3. 视图 - 登录、注册和经过验证的“仪表板”屏幕。
  4. 社交身份验证处理程序


使用 Express 和 Passport,因为我们不会重新发明轮子,所以我们得到了 150 行非常非常枯燥和重复的代码: https://github.com/supertokens/auth-express/blob/master/index.mjs 。下一节将对代码中发生的事情进行表面级别的解释,因此如果您已经熟悉这些概念,请随意跳过。无论如何,express 应用程序是一个 PoC。


让我们快速剖析一下:

在屏幕上渲染内容

在我看来,有两种方法可以解决这个问题 - 从渲染开始,然后转到身份验证路由,或者反过来。主要是偶然,我最终成为了一个重度 FE 的普通人(如果你想知道的话,我仍然可以执行 SQL),所以我将从“在屏幕上渲染内容”的方法开始。


由于这是一个 PoC,我们不会完全采用 React-fancy。使用ejs的普通 SSR 就足够了: https://github.com/supertokens/auth-express/tree/master/views

添加路线

基于一些passport.js示例,但进一步简化,我们需要以下内容:

  1. 一些依赖项: npm i passport passport-local express-session 。让我们简单介绍一下每个依赖项:

    1. Passport.js——用于 express 和 node 的 OG 身份验证中间件。
    2. Passport-local - Passport 的身份验证策略;模块中的身份验证策略,用于处理特定身份验证方法的身份验证过程 - 在本例中,使用用户名(电子邮件)和密码进行本地登录。
    3. express-session - 一个管理会话数据的中间件,允许您在 HTTP 请求之间存储和持久化用户会话。它的工作原理是为每个客户端分配一个唯一的会话 ID,该 ID 存储在客户端的 cookie 中,并用于在服务器上检索会话数据。
  2. 存储用户的地方(上面链接的示例使用内存数组): https://github.com/supertokens/auth-express/blob/master/index.mjs#L13

  3. 配置我们的护照实例和 LocalStrategy 实例来处理传入的用户查找请求: https://github.com/supertokens/auth-express/blob/master/index.mjs#L18

  4. 初始化护照( https://github.com/supertokens/auth-express/blob/master/index.mjs#L60 )和 express-session( https://github.com/supertokens/auth-express/blob/master/index.mjs#L69 )。


当然很冗长。难吗?我不这么认为,至少在将其作为玩具实现的意义上不难。但我们不久前不再使用电子邮件/密码组合。让我们来看看在现有的基础上添加社交提供商有多难。


对于这里的示例提供商,我决定使用 GitHub,原因很简单 - 如果您决定完全跟随,它是最容易上手的提供商之一(看着你,谷歌)。


如果您决定完全按照说明进行操作,这里有一个链接描述了如何获取这些 GitHub 密钥: https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app哦,顺便说一句,如果您担心的话,repo 中的密钥无效 ;)

在我们的 PoC 中集成 GitHub OAuth2

首先,我们需要一个依赖项, npm i passport-github2 。passport -github2是 Passport 的身份验证策略,允许我们与 GitHub 的 OAuth2 API 集成。


一些处理程序( https://github.com/supertokens/auth-express/blob/master/index.mjs#L122-L133 )和配置( https://github.com/supertokens/auth-express/blob/master/index.mjs#L29-L45 )之后,好了,就这些了。复杂吗?可能不会。繁文缛节?没错。无聊吗?绝对无聊。特别是如果你不得不一遍又一遍地做这件事。这是一个已经解决的问题;正如我们已经确定的那样,重新发明轮子往往不是最好的利用时间的方式。


伟大的想法

到目前为止,我们可能已经同意 Auth 并不难构建。因此,它并不是只有那些说着 JWT 神秘语言的白胡子巫师才能理解和实现的神奇事物。


不,事实上,我认为作为一名开发人员,应该了解身份验证的基本工作原理。但我经常看到有人声称并非如此——比如“相信我,兄弟,我们可以帮你处理这个问题”。当然,我同意在大多数情况下,自己进行身份验证是浪费时间。但这并不难构建,也绝对不是什么神秘的事情。真正棘手的是围绕身份验证和用户体验的一切。


考虑一下 - 在上面的例子中,我们有一个有效的身份验证方法。有点。但它不能做的事情如下(文章开头也提到过):

  • 2FA、MFA
  • 密码重置
  • 数百家 OAuth 提供商各有特色
  • 用户管理
  • 不同提供商的帐户合并
  • 涵盖所有可能的边缘情况和潜在的安全漏洞
  • ...我还可以继续


我们可能可以实现其中的每一项。单独来看,每一项都可能很简单。但加起来就很复杂了。所以,这不一定是实现——而是维护它、对它负责、及时了解标准、安全漏洞等等。另外,请举手——你们中有多少人喜欢阅读 RfC?如果我们在聚会上,我想我不会看到很多人举手。


我的观点是,从整体上看,身份验证并不容易。当然,我们可以像上面那样轻松地拼凑一些东西来制作 PoC。但这并不是什么魔法,也不是无法理解的,请不要说它是魔法。在我看来,这种思路(和营销)对整个行业都是有害的。


因此,自然而然的后续问题是——你什么时候应该自己动手?

玩具项目、独立和教育追求

...当然可以。我甚至会鼓励这样做。你从实践中学到很多东西,所以为什么不呢?如果你的独立/玩具项目或博客发展到拥有相当大的用户群或追随者,请将其切换到服务、自托管解决方案或其他东西。毕竟,你现在有一个产品,毫无疑问,你的时间最好花在构建该产品上,而不是维护授权上。

初创企业

一般来说,如果你正在开发产品,不要自己动手做身份验证。这就像重新发明一个非常无聊和繁琐的轮子。你有很多选择。另外,你正在开发一些东西,对吧?如果你的产品没有身份验证,我们为什么要进行这样的对话?

扩大规模及以上(无论我们如何定义它们)

不要。原因和初创公司一样 - 但在这里肯定更适用。


您可能已经明白我的意思了。我认为,“身份验证很难”在用作概括性陈述时是一种营销宣传。您可以理解身份验证,可以构建身份验证,但它很无聊,维护起来也不好玩,而且这是一个已经解决的问题。因此,它可以被视为一种商品 — 您可以从货架上挑选任何一种口味(下面有一些选项)。

自托管和 FOSS 格局

对于那些想要拥有自己的堆栈的人(就像我一样),你也有很多选择:

身份验证库

  • Passport.js,上面详细介绍过
  • Lucia - 一个适用于现代 Web 应用程序的简单灵活的身份验证库,注重开发人员体验和易用性。
  • Auth.js - 一个轻量级且可定制的 Node.js 身份验证库,旨在轻松集成到各种框架和应用程序中。最初是作为 Next 的一个库。

身份验证服务器

  • Keycloak - 一种开源身份和访问管理服务器,提供单点登录 (SSO)、身份代理和用户联合等功能。
  • SuperTokens (请参阅上面的免责声明) - 一种开源身份验证解决方案,提供预构建的功能,如会话管理、社交登录和电子邮件/密码身份验证,重点关注安全性和简单性。
  • FusionAuth - 一个面向开发人员的灵活身份验证平台,提供用户管理、多因素身份验证 (MFA) 和单点登录 (SSO) 等功能。
  • Authelia - 提供多因素身份验证 (MFA) 和 SSO 的开源身份验证服务器,旨在使用反向代理保护应用程序的安全。

存储 + 授权

  • Supabase - 一个开源后端即服务 (BaaS) 平台,提供数据库、身份验证和实时功能,旨在作为 Firebase 的替代品。
  • Pocketbase - 一个结合数据库、身份验证和文件存储的开源后端解决方案,旨在简化现代网络和移动应用程序的开发。


因此,即使您不想使用第三方软件进行 Auth,您也可以根据您的需求和偏好选择现成的开源软件并使用它。

结论:身份验证是开发人员的“繁文缛节”

我的“最大”收获是避免重新发明轮子,尤其是当它是一个已解决的问题时,就像 auth 一样。了解上述轮子,用它们做实验,做一个玩具轮子,并理解它。但请不要把它当作难以理解和建造的东西来推销。教育,不要把关。