2016 年 6 月

第 31 卷,第 6 期

此文章由机器翻译。

工作的程序员如何成为平均: Passport

通过 Ted Neward | 2016 年 6 月

Ted Neward欢迎回来,MEANers。

我一直在做大量的服务器端工作,一切变得越来越接近首先移到内容的客户端的时间。我这样做之前,不过,没有绝对需要讨论之前我完全可以进行转换的一件事情。具体而言,我需要能够支持用户。大多数应用程序 (如果尚未全部用户,此点) 都需要某种类型的用户身份验证机制通常建立用户的标识,以便您可以限制显示它们或允许其在系统中执行的操作的选项的数据。

虽然它总是很容易"滚动自己,"节点社区中这只是因此 2010年 ! 任何这些种类的困境的正确答案总是"转到 npm,",在这种情况下,解决身份验证系统的分布情况远入选方是一个名为 Passport 的 Node.js 库。

Passport

到此时为止,变得很简单找出哪些前几个步骤适用于使用 Passport 库;找到的 npm 包名称和""npm 安装。 通过搜索联机 npm 注册表中,或通过访问 Passport 主页,可以发现 npm 程序包名称。但是,第一次访问 PassportJS.org 产生两个有趣的信息 ︰ 一、,与每个其他 Node.js 包主页曾经编写过,相反"npm install"命令不存在就在这里的首页; 上两个,从该 Passport 显然这一概念的"策略",并且初始屏幕很重要。

其原因很简单: 当您说时,"就可以对用户的凭据进行身份验证"这是实际的含糊不清的语句。不仅有各种不同的凭据可能用于进行身份验证、 没有有 1000 多个或多个不同的凭据中存储 (按照服务器) 对用户可能会验证。Passport 希望成为任何一种针对任何类型的凭据存储区身份验证的解决方案 — Facebook、 LinkedIn、 Google 或您自己的本地数据库 — 并使用各种不同的凭据,从通过可能想到的 JSON Web 令牌对 HTTP 持有者的页眉和任何其他用户名/密码。

这意味着,则该 Passport 不只是一个包中;还有一个核心 passport 程序包,然后就是为如何 Passport 执行实际工作中进行身份验证策略 (其中,事实上,在撰写本文时 307)。所选的策略 (或策略 — 我会谈到稍后) 定义实际的程序包所需,这又定义了要安装的内容。(但事实上在广告时间在这里,Passport 不会实际定义核心程序包将由其他策略所涉及的"passport"以便您可以通过执行"npm install-保存 passport"深入讨论的策略详细信息之前获取事情上的一个跳转)。

大家好本地

(特别是对于针对内部用户数据库/凭据存储区正在生成的系统) 的无疑最常见策略是"local"的策略。这是传统,"客户端发送用户名和密码,并比较对 … 嗯,无论你存储的用户名和密码。" 它无疑是还未几乎尽可能安全的一些其他的策略,但它是一个好的开始。

现在,在代码中我一直在使用、 已有说明,否则不进行身份验证。因此,让我们简单起见只是进行硬编码固定的用户名/密码到位。一旦您看到 Passport 的工作原理,就相对较容易看出会去做比较,因此我让它们保持数据库查找的代码。

具有决定我想要使用的 passport 本地策略,在我开始使用"npm install-保存 passport 本地"来获取的必要 passport 位中的位置。(请记住,"-保存"参数将它放在包清单文件,以便它将获取自动跟踪作为正式的依赖关系。)

在安装之后,我需要做三件事 ︰ one,配置 Passport 使用给定的策略;两个,建立 HTTP URL 路由到的用户将发送身份验证请求;三种模型,设置 Express 中间件以要求在允许用户实际访问 HTTP URL 问题前的身份验证。

配置

首先,获取 Passport 时应用程序中加载第一个。假定已安装 passport 和 passport 本地,我需要将它们加载到 app.js 通过常用的脚本需要神奇功能 ︰

var express = require('express'),
  bodyParser = require('body-parser'),
  // ...
  passport = require('passport'),
  LocalStrategy = require('passport-local').Strategy;

请注意,LocalStrategy 设置略有不同;如通过 MongoClient 之前,您实际上分配 LocalStrategy 访问外 require 调用时返回的对象的"策略"字段的结果。此 is'nt 常见 Node.js,但它不是因此少见,就可以使用唯一的。LocalStrategy 这种情况下要作为类进行实例化的一种 (或作为接近其作为 JavaScript 通常可以获得)。

我还需要告诉速成版环境,护照就是在作业 ︰

var app = express();
app.use(bodyParser.json());
app.use(passport.initialize());

调用 initialize 非常容易理解;它将准备 Passport 若要准备接收传入的请求。通常情况下,将有类似的调用到 passport.session 设置每个用户会话,类似于在 ASP.NET 中,但如什么我要构建在这里,经常有必要,或比较理想 HTTP api 看到的内容 (我将讨论这一点在某种程度)。

挑战

接下来,我需要建立 Passport 接收身份验证请求时将调用的回调。此回调将执行查找用户的工作,在传递,验证该密码。(或者,在更真实方案中,查找用户和验证加盐的密码哈希是否与当前存储在数据库中的加盐的密码哈希相同 — 但这是实际上更多的 Passport 本身范围之外。) 通过调用 passport.use 并使用随回调嵌入,如中所示的策略的一个实例中传递 图 1

图 1 建立回调

passport.use(new LocalStrategy(
  function(username, password, done) {
    debug("Authenticating ",username,",",password);
    if ((username === "sa") && (password == "nopassword")) {
      var user = {
        username : "ted",
        firstName : "Ted",
        lastName : "Neward",
        id : 1
      };
      return done(null, user);
    }
    else {
      return done(null, false, { message: "DENIED"} );
    }
  }
));

下面将进行多个操作。首先,会调用该回调时,Passport 已经分析传入的请求和提取的用户名和密码中传递给此回调的工作。为 LocalStrategy,Passport 假定通过分别名为用户名和密码,参数中传递这些值。(这可以配置在 LocalStrategy 构造调用中,如果这些都不是可接受。)

其次,验证的实际机制是完全之外 Passport 的管辖;它假定策略将执行验证,并在这种情况下,"local"战略交,完全应用程序代码。在此示例中,只需检查根据硬编码值,但在更多的常规情况下,这将是对其用户名匹配已传入了哪些用户,然后再针对密码检查执行 Mongo 的查找。

第三,按照通常的 Node.js 中间件样式,成功或失败发出信号 done 函数中,使用带有参数传递,该值指示是否成功或失败发生的时间。成功意味着第二个参数是一个将放置在传递管道; 向下进一步 Express request 对象内的用户对象失败将导致 Passport 询问快速返回 401 (未经授权) 响应,并且可根据需要包括失败消息,通常用于对用户界面"闪存"消息。(如果不使用闪存消息,消息是有效地丢弃。)

结果

现在,剩下的就是要配置的身份验证会发生路由 ︰

app.post('/login',
  passport.authenticate('local', { session: false }),
  function(req, res) {
    debug("user ", req.user.firstName, " authenticated against the system");
    res.redirect("/persons");
  });

Passport 特别并不关心 URL 模式是什么,它完成实际的身份验证;/login 是只是一种约定,但会完全合理 /signin 或 /user/auth 或任何半打其它种类。关键是解决此路由是调用 passport 的第一步函数,哪种策略来使用 (local) 是否为使用每个用户会话 cookie (即,前面已经提到,不特别适合 API),并传入和实际的函数进行调用身份验证成功进行身份验证。在这里,该函数只需记录一条消息,若要调试,然后重定向用户的人员列表存储在数据库中。

现在,我可以测试此通过传入任一窗体发布内容或通过发送 JSON 内容; 中由于这是一个 API,则很可能更好、 更轻松地在 JSON 数据包中发送 ︰

{ "username" : "sa" , "password" : "nopassword" }

如果用户名和密码相匹配,成功,并返回到 /persons 302 重定向;如果没有,则 401 响应传递回。Works !

将流量重定向

在事实中,它的一种常见模式 (当构建使用速成版的传统服务器端 Web 应用程序) 成功进行身份验证将使用户进入给定的路由,而故障应使用户进入一个新页,并为此,Passport 允许更简单的方法进行身份验证的回调 ︰

app.post('/login',
  passport.authenticate('local', { successRedirect: '/',
                                   failureRedirect: '/login',
                                   failureFlash: true })
);

在这里,如果成功,Passport 会自动重定向到"/"的 URL,并在失败时,返回到"/ 登录名"的 URL,并且使用闪存条消息,表明用户未能成功登录的 (在这种情况下)。

对于一个 API,不过,它是更常用来将返回的 JSON 表示形式的用户对象提交给客户端的显示和编辑。请记住,但是,程序执行与安全相关的任何操作或区分应不断发回的这一部分 — — 尤其是没有密码。浏览器是在提供客户端的调试实用程序,所有非常有帮助,结果是,任何攻击者可以非常轻松地访问在浏览器的内存和开始编辑即可对其核心内容中保留该用户对象。这可能是错误。(这些 JSON 对象可能还被篡改"中处理,"提示最 Node.js 基于 API 的系统来运行 over HTTPS,而不是 HTTP。幸运的是,大多数情况下,配置 Express 运行通过 HTTPS 而不是 HTTP 是更多的练习云配置中比任何编程更改。) 结果是,密码绝不会脱离服务器上,而"roles"(对于基于角色的授权系统) 应始终检查从数据库中不能从传入请求的用户对象。

顾名思义,但是,现在 API 客户端将需要通过身份验证凭据,每次"/ 登录名"命中路由,请和凭据是不会检查对任何其他路由。我当然可能会使每个路由身份验证检查 (并且应该细想它),而我可能不想必须为每个方法调用的一部分传递凭据。

替代方案

Passport 具有涵盖"黑桃,"这个想法,如他们所说的。

首先,您始终可以返回到打开的会话;打开会话时,将创建 Passport,唯一标识符和传回 HTTP 响应作为 cookie 的一部分。客户端然后需要该 cookie 退回作为每个后续请求的一部分。此时,在服务器端的主要要求是需要知道如何将用户对象转换为一个标识符,并切换回; Passport 库它们调用此序列化和反序列化一个用户,并为每个这些两个 Passport 终结点需设置方法的回调 ︰

passport.serializeUser(function(user, done) {
  done(null, user.id);
});
passport.deserializeUser(function(id, done) {
  User.findById(id, function(err, user) {
    done(err, user);
  });
});

SerializeUser 函数旨在为用户为 Passport 提供唯一标识符,(因此,我获取它出 user.id 字段) 和 deserializeUser 函数恰好相反 (因此,我使用数据库查找中作为一个整体用户对象的主键作为传入的 id)。

您可以打开会话对于大多数而言,如果不是全部 Passport 策略,但在它时起作用的常规服务器生成 HTML 以浏览器直接解释。Api 倾向于不使用 cookie 几乎尽可能多,特别是因为高达或通常比基于浏览器的客户端,通过本机移动应用程序客户端通常命中 Api。

第二种方法使用不同的 Passport 策略,它依赖于为客户端和服务器"确认密码"。这再传递不同的方式。在某些情况下,系统维护一组已知的已颁发"API 密钥,",则必须为每个请求的一部分提供该密钥。这是非常常见的多种第三方 REST 服务,但它具有严重漏洞,因为如果攻击者可以获得密钥,攻击者可以会伪装成客户端之前客户端重置的密钥。Passport 为此; 提供了一种策略使用"npm install-保存 passport localapikey。" 其行为的本地策略的方式大致相同,只不过现在的策略身份验证方法将查找数据库中,而不是用户名和密码的 API 密钥。

类似的方法利用了 JSON Web 令牌 (Jwt),从而更安全,但这需要多长篇幅来解释比我这里; 有"npm install-保存 passport jwt"会带入项目。Jwt 是各种的已打包不同的数据元素,其中一个可以是各种的共享的机密 (à la API 密钥或密码),但可以验证特定颁发者、 访问群体和多集。

或者,目标可能是不存储任何类型的凭据,而是依赖 (如 Facebook、 Google、 Twitter、 LinkedIn 或任意多个几百个其他流行网站) 的第三方系统进行身份验证。Passport 已涵盖在这里,以及使用特定策略的每个站点单独进行,作为通用 OAuth 2.0 (和 OpenID,对于使用,这些网站) 以及策略。

我认为该点变得越来越明确 ︰ 您可以想到的是身份验证系统,Passport 已为其定义的策略。只需"npm install、"设置配置,将授权调用放在速成版路由并开始。

顺便说一下,似乎必须指出的一点是服务通过 Internet 将为所有这些身份验证问题提供单点访问控制。这些"身份验证作为-服务"服务变得更受欢迎用户定期使用的站点数扩增以及变得越来越多的管理难题。我的收藏夹,Auth0 (该类却有一些 ex Microsoft 用户在公司的技术方面中),之一是赞助商 Passport 项目中,以及其图标和徽标出现严格分散在整个 Passport 站点。我强烈建议仔细研究一下它,如果项目尚不具有预先确定的身份验证策略结构 (例如旧系统或针对 Facebook 或收存箱,或你拥有将集成)。

总结

Passport 无疑是有史以来所开发,在任何语言或平台之间的最成功的身份验证项目。它管理以提供必要的身份验证"挂钩"时在打开的实际的一种身份验证时所需进行控制,但在延迟并时不必执行所有的繁重的任务。策略方法意味着它是可以无限扩展,并可以适应任何类型的可能出现时,即使 20 年在未来的新身份验证方案。(不肯定会为你 — 事实上,仍此 JavaScript 都从现在开始运行 20 年的经验。观看。)

但 Passport 定义几乎尽可能通过它不会为它的作用; 所做的操作它完全 punts 基于角色的授权任何理论,它不会尝试解决任何种类的加密或加密。护照就是所有有关凭据检查,这当然是通过该点使得大量更清晰的名称 — Passport 就像当我需要传输到欧洲,我需要显示以证明我是美国公民我 passport,要求用户显示他们的凭据,以便它们可以证明它们是在系统中的合格成员。

再次重申,我发现自己外空间和时间,因此现在 … 祝您编码愉快 !


Ted Neward是基于西雅图的 polytechnology 顾问、 发言人和导师。他曾写过 100 多篇文章,是 F # MVP 和具有创作,并且与人合著过十几本书。与他联系。 ted@tedneward.com。 如果您感兴趣他参与使用你的团队,或阅读他的博客 blogs.tedneward.com

感谢以下技术专家对本文的审阅: Shawn Wildermuth