飞码网-免费源码博客分享网站

点击这里给我发消息

在Node.js中使用Passport进行本地认证|-JavaScript教程

飞码网-免费源码博客分享网站 爱上飞码网—https://www.codefrees.com— 飞码网-matlab-python-C++ 爱上飞码网—https://www.codefrees.com— 飞码网-免费源码博客分享网站

构建Web应用程序时,常见的要求是实施登录系统,以便用户可以在访问受保护的视图或资源之前进行身份验证。幸运的是,对于那些正在构建Node应用程序的人来说,有一种名为Passport的中间件,可以将其放入任何基于Express的Web应用程序中,从而仅用几个命令即可提供身份验证机制。

在本教程中,我将演示如何使用Passport通过MongoDB后端实现本地身份验证(即使用用户名和密码登录)。如果您希望通过Facebook或GitHub等实施身份验证,请参阅本教程。

与以往一样,本文的所有代码都可以在GitHub上下载。

先决条件

要继续学习本教程,您需要在计算机上安装Node和MongoDB。

您可以通过前往官方的Node下载页面并获取适用于您系统的正确二进制文件来安装Node或者,您可以使用版本管理器-一个程序,该程序可让您安装Node的多个版本并在它们之间随意切换。如果您愿意这样做,请查阅我们的快速提示“使用nvm安装Node.js的多个版本”。

MongoDB有各种版本。我们感兴趣的是MongoDB社区版。

该项目的主页有出色的文档,我不会在这里重复。相反,我将为您提供指向每个主要操作系统的说明的链接:

  • 在Windows上安装MongoDB社区版
  • 在macOS上安装MongoDB Community Edition
  • 在Ubuntu上安装MongoDB Community Edition

如果您使用非基于Ubuntu的Linux版本,则可以查看此页面以获取其他发行版的安装说明。通常也可以通过官方Linux软件渠道获得MongoDB,但是有时它将引入过时的版本。

注意:下载MongoDB无需输入名称和地址。如果出现提示,通常可以关闭该对话框。

如果您想快速使用MongoDB,请查阅我们的初学者指南“ MongoDB简介”。

身份验证策略:会话与JWT

在开始之前,我们先简要介绍一下身份验证选择。

如今,在线上的许多教程都将选择使用JSON Web令牌(JWT)进行基于令牌的身份验证。这种方法可能是当今最简单,最受欢迎的方法。它将部分身份验证职责委托给客户端,并使它们签署随每个请求一起发送的令牌,以保持用户身份验证。

基于会话的身份验证的时间更长了。此方法将身份验证的权重委派给服务器。它使用cookie,并查看Node应用程序和数据库一起工作以跟踪用户的身份验证状态。

在本教程中,我们将使用基于会话的身份验证,这是护照本地策略的核心。

两种方法都有其优点和缺点。如果您想更多地了解两者之间的区别,此Stack Overflow线程可能是一个不错的起点。

创建项目

一旦所有必备软件安装完毕,我们就可以开始。

我们将从为应用程序创建文件夹开始,然后在终端上访问该文件夹:

mkdir AuthApp
cd AuthApp

要创建节点应用,我们将使用以下命令:

 
npm init

系统将提示您提供一些有关Node的信息package.json只需继续单击Return以接受默认配置(或使用-y标志)。

设置快递

现在我们需要安装Express。转到终端并输入以下命令:

npm install express

我们还需要安装body-parser中间件,该中间件用于解析Passport用于验证用户身份的请求正文。而且,我们需要安装快速会话中间件。

来做吧。运行以下命令:

npm install body-parser express-session

完成后,index.js在应用程序的根文件夹中创建一个文件,并向其中添加以下内容:

/*  EXPRESS SETUP  */

const express = require('express');
const app = express();

app.use(express.static(__dirname));

const bodyParser = require('body-parser');
const expressSession = require('express-session')({
  secret: 'secret',
  resave: false,
  saveUninitialized: false
});

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressSession);

const port = process.env.PORT || 3000;
app.listen(port, () => console.log('App listening on port ' + port));

首先,我们require通过调用express()来Express并创建Express应用。然后,我们定义用于提供静态文件的目录。

在下一行中,我们看到require了主体解析器中间件,它将帮助我们解析主体的请求。我们还添加了快速会话中间件,以帮助我们保存会话cookie。

可以看到,我们在配置express-session时使用secret签名会话ID cookie(您应该在此处选择一个唯一值),以及另外两个字段resave和saveUninitialized。resave字段强制将会话保存回会话存储,而saveUninitialized字段强制将“未初始化”的会话保存至存储。要了解有关它们的更多信息,请查看它们的文档,但是到目前为止,对于我们而言,我们想要保留它们就足够了false

然后,我们process.env.PORT将端口设置为环境端口变量(如果存在)。否则,我们将默认设置为3000,这是我们将在本地使用的端口。这为您提供了足够的灵活性,可以从开发直接切换到生产环境,在该生产环境中,端口可能由服务提供商(例如Heroku)设置。在此之下,我们使用设置的port变量和一个简单的日志调用app.listen(),以使我们知道它一切正常,并且应用程序在哪个端口上侦听。

这就是Express设置的全部内容。现在开始设置Passport

设置Passport

首先,我们使用以下命令安装Passport:

npm install passport

然后,我们需要在index.js文件底部添加以下几行

/*  PASSPORT SETUP  */

const passport = require('passport');

app.use(passport.initialize());
app.use(passport.session());

在这里,我们需要passport直接在Express应用程序中对其进行初始化,并将其连同其会话身份验证中间件一起进行初始化。

创建一个MongoDB数据存储

由于我们假设您已经安装了Mongo,因此您应该能够使用以下命令启动Mongo Shell:

mongo

在外壳程序中,发出以下命令:

use MyDatabase;

这只是创建一个名为的数据存储MyDatabase

将终端留在那儿;我们稍后再讲。

用Mongoose将Mongo连接到Node

现在我们有了一个包含记录的数据库,我们需要一种从应用程序与之通信的方法。我们将使用猫鼬来实现这一目标。我们为什么不只使用普通的Mongo?好吧,正如Mongoose开发人员喜欢在其网站上说,A href =” https://mongoosejs.com/docs/unstable/index.html”>:

编写MongoDB验证,强制转换和业务逻辑样板很麻烦。

猫鼬只会使我们的生活更轻松,代码更优雅。

让我们继续使用以下命令进行安装:

npm install mongoose

我们还将使用Passport-local-mongoose,它将简化Mongoose和Passport之间用于本地身份验证的集成。它将在我们的模式中添加hashandsalt字段,以存储哈希密码和salt值。很好,因为密码绝不应该以纯文本格式存储在数据库中。

让我们安装软件包:

npm install passport-local-mongoose

现在我们必须配置猫鼬。希望您现在就知道了演练:将以下代码添加到index.js文件底部

/* MONGOOSE SETUP */

const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');

mongoose.connect('mongodb://localhost/MyDatabase',
  { useNewUrlParser: true, useUnifiedTopology: true });

const Schema = mongoose.Schema;
const UserDetail = new Schema({
  username: String,
  password: String
});

UserDetail.plugin(passportLocalMongoose);
const UserDetails = mongoose.model('userInfo', UserDetail, 'userInfo');

在这里,我们需要以前安装的软件包。然后,我们使用连接到数据库,mongoose.connect并为其提供数据库的路径。接下来,我们将使用Schema定义我们的数据结构。在这种情况下,我们将UserDetail使用usernamepassword字段创建模式

最后,我们将其添加passportLocalMongoose为Schema的插件。这将成为我们之前讨论的魔术的一部分。然后,我们根据该架构创建一个模型。第一个参数是数据库中集合的名称。第二个是对Schema的引用,第三个是我们为Mongoose内的集合分配的名称。

这就是猫鼬设置的全部内容。现在,我们可以继续实施我们的Passport策略。

实施本地认证

最后,这就是我们来这里要做的!让我们设置本地身份验证。如您在下面看到的,我们将只编写将为我们设置代码的代码:

/* PASSPORT LOCAL AUTHENTICATION */

passport.use(UserDetails.createStrategy());

passport.serializeUser(UserDetails.serializeUser());
passport.deserializeUser(UserDetails.deserializeUser());

这里有很多魔术。首先,我们passport通过调用createStrategy()我们的UserDetails模型(礼貌性)来利用本地策略,该模型passport-local-mongoose会处理所有问题,因此我们不必制定策略。很方便。

然后,我们使用serializeUseranddeserializeUser回调。第一个将在身份验证时被调用,它的工作是使用我们传递给用户实例的信息序列化用户实例,并通过cookie将其存储在会话中。第二个请求将在随后的每个请求中调用以反序列化该实例,并为其提供唯一的cookie标识符作为“凭据”。您可以在Passport文档中阅读有关此内容的更多信息。

路线

现在,让我们添加一些路线以将所有东西捆绑在一起。首先,我们将添加一个最终包。转到终端并运行以下命令:

npm install connect-ensure-login

connect-ensure-login软件包是确保用户已登录的中间件。如果收到未经身份验证的请求,该请求将被重定向到登录页面。我们将用它来保护我们的路线。

现在,将以下内容添加到的底部index.js

/* ROUTES */

const connectEnsureLogin = require('connect-ensure-login');

app.post('/login', (req, res, next) => {
  passport.authenticate('local',
  (err, user, info) => {
    if (err) {
      return next(err);
    }

    if (!user) {
      return res.redirect('/login?info=' + info);
    }

    req.logIn(user, function(err) {
      if (err) {
        return next(err);
      }

      return res.redirect('/');
    });

  })(req, res, next);
});

app.get('/login',
  (req, res) => res.sendFile('html/login.html',
  { root: __dirname })
);

app.get('/',
  connectEnsureLogin.ensureLoggedIn(),
  (req, res) => res.sendFile('html/index.html', {root: __dirname})
);

app.get('/private',
  connectEnsureLogin.ensureLoggedIn(),
  (req, res) => res.sendFile('html/private.html', {root: __dirname})
);

app.get('/user',
  connectEnsureLogin.ensureLoggedIn(),
  (req, res) => res.send({user: req.user})
);

在顶部,我们需要connect-ensure-login我们待会儿会再谈这个。

接下来,我们设置一条路由来处理对该/login路径的POST请求在处理程序内部,我们使用passport.authenticate方法,该方法尝试使用其接收的作为第一个参数的策略进行身份验证(在本例中为)local如果身份验证失败,它将把我们重定向到/login,但是它将添加一个查询参数— info—将包含一条错误消息。否则,如果身份验证成功,它将把我们重定向到该'/'路由。

然后,我们设置/login路由,该路由将发送登录页面。为此,我们使用res.sendFile()并传入文件路径和我们正在处理的根目录,即__dirname

/login任何人都可以访问路线,但我们的下一条路线则无法访问。//private路线中,我们将发送各自的HTML页面,您会在这里注意到一些不同的地方。在回调之前,我们要添加connectEnsureLogin.ensureLoggedIn()呼叫。这是我们的路线守卫。它的工作是验证会话,以确保允许您查看该路由。您现在是否看到我之前所说的“让服务器承担繁重的工作”的意思?我们每次都对用户进行身份验证。

最后,我们需要一条/user路线,该路线将返回一个包含我们用户信息的对象。这只是向您展示如何从服务器获取信息。我们将从客户端请求此路由并显示结果。

谈到客户,现在就开始做。

客户端

客户应该很简单。我们将创建一些HTML页面和一个CSS文件。让我们从主页或索引开始。在项目根目录中,创建一个名为的文件夹,html并添加一个名为的文件index.html向其中添加以下内容:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title> Home </title>
  <link rel="stylesheet" href="css/styles.css">
</head>

<body>
  <div >
    <h1 ></h1>
    <a href="/private">Go to private area</a>
  </div>

  <script>
    const req = new XMLHttpRequest();
    req.onreadystatechange = function () {
      if (req.readyState == 4 && req.status == 200) {
        const user = JSON.parse(req.response).user;
        document.getElementById("welcome-message").innerText = `Welcome ${user.username}!!`;
      }
    };
    req.open("GET", "http://localhost:3000/user", true);
    req.send();
  </script>
</body>
</html>

在这里,我们有一个空h1标记,我们将在其中放置欢迎消息,并在其下方提供指向的链接/private这里的关键部分是script底部标记,我们将在标记处获取用户名以创建欢迎消息。

这分为四个部分:

  1. 我们使用实例化请求对象new XMLHttpRequest()
  2. 我们onreadystatechange使用获得答案后将要调用的函数来设置属性。在回调中,我们正在检查是否有成功的响应,如果有,我们将解析响应,获取用户对象(我们在/user路由中发送的对象,还记得吗?),然后找到welcome-message将其设置innerText为我们元素。user.username
  3. 我们open()GET请求,用户URL和我们设定的最后一个参数true,使之asynchronous
  4. 最后,我们send()的要求。

现在,我们将创建登录页面。和以前一样,在HTML文件夹中创建一个名为的文件,login.html并向其中添加以下内容:

<!DOCTYPE html>
<html lang="en">
<head>
  <title> Login </title>
  <link rel="stylesheet" href="css/styles.css">
</head>

<body>
  <form action="/login" method="post">
    <div >
      <h3>Login</h3>
    </div>
    <div >
      <label>Username:</label>
      <input type="text" name="username" />
      <br />
    </div>
    <div >
      <label>Password:</label>
      <input type="password" name="password" required />
    </div>
    <div >
      <input  type="submit" value="Submit" required />
    </div>
    <label ></label>
  </form>

  <script>
    const urlParams = new URLSearchParams(window.location.search);
    const info = urlParams.get('info');

    if(info) {
      const errorMessage = document.getElementById("error-message");
      errorMessage.innerText = info;
      errorMessage.style.display = "block";
    }
  </script>
</body>
</html>

在此页面上,我们有一个简单的登录表单,带有usernamepassword字段,以及一个Submit按钮。在此之下,我们有一个标签,其中将显示所有错误消息。请记住,这些包含在查询字符串中。

这次script底部标签要简单得多。我们正在实例化一个URLSearchParams传递window.location.search属性对象,该属性在我们的URL中包含参数字符串。然后,使用该URLSearchParams.get()方法,传入我们要查找的参数名称。

在这一点上,我们是否有一条信息消息。因此,如果这样做,我们将获取error-message元素并将其设置innerText为该消息所对应的内容,然后将其style.display属性设置block假定默认情况下它具有display: "none",这将使其可见

现在设置私人页面。再次在HTML文件夹中创建一个名称为的文件,private.html并添加以下内容:

<!DOCTYPE html>
<html lang="en">
<head>
  <title> Private </title>
  <link rel="stylesheet" href="css/styles.css">
</head>

<body>
  <div >
    <h2>This is a private area</h2>
    <h3>Only you can see it</h3>
    <a href="/">Go back</a>
  </div>
</body>
</html>

超级简单。只需一条简单的消息和一个Go back链接即可将我们带回到首页。

HTML就是这样,但是您可能已经注意到,我们正在引用CSS文件的head标签。现在添加该文件。css在项目的根目录中创建一个名为的文件夹,并向其中添加一个styles.css文件,其内容如下:

body {
  display: flex;
  align-items: center;
  background: #37474F;
  font-family: monospace;
  color: #cfd8dc;
  justify-content: center;
  font-size: 20px;
}

.message-box {
  text-align: center;
}

a {
  color: azure;
}

.field {
  margin: 10px;
}

input {
  font-family: monospace;
  font-size: 20px;
  border: none;
  background: #1c232636;
  color: #CFD8DC;
  padding: 7px;
  border: #4c5a61 solid 2px;
  width: 300px;
}

.submit-btn {
  width: 100%
}

.title {
  margin: 10px 0px 20px 10px
}

#error-message {
  color: #E91E63;
  display: block;
  margin: 10px;
  font-size: large;
  max-width: fit-content;
}

这将使我们的页面看起来足够不错。让我们来看看!

抓住一个指向项目根目录的终端,然后运行以下命令:

node index.js

现在,在浏览器中导航到http:// localhost:3000 /。您应该被重定向到登录页面。如果您尝试转到http:// localhost:3000 / private,它将重新将您重定向到登录页面。有我们的选路员在做它的工作。

在终端窗口中Ctrl+C停止我们的服务器。然后返回index.js文件,并在文件底部添加以下行:

/* REGISTER SOME USERS */

UserDetails.register({username:'paul', active: false}, 'paul');
UserDetails.register({username:'jay', active: false}, 'jay');
UserDetails.register({username:'roy', active: false}, 'roy');

这使用本地护照猫鼬register方法为我们加密码。我们只需要以纯文本形式传递它。

现在我们开始node index.js将创建用户。您现在应该注释最后几行。

还记得我们打开的MongoDB shell终端吗?回到它并输入:

db.userInfo.find()

这应该显示您的三个用户,并且您可以看到,salt和hash现在占据了终端上很大一部分空间。

这就是应用程序运行所需的一切。大功告成!

返回浏览器,尝试使用我们输入的凭据之一登录,您将在其中看到带有给定用户名的登录消息。

下一步

我们仅添加了使该应用程序正常运行所需的模块-仅此而已。对于生产应用程序,您需要添加其他中间件并将模块中的代码分开。您可以将其视为挑战,以建立一个干净且可扩展的环境并将其发展为有用的东西!

您应该尝试的第一个也是最简单的方法是logout使用Passport的req.logout()方法添加。

然后,您可以尝试实现注册流程。您需要注册表格和交谈的途径。您应该使用UserDetails.register()前面添加的模板。对于电子邮件确认,您应该签出nodemailer。

您可以做的另一件事是尝试将这些概念应用于单个页面应用程序。也许使用Vue.js及其路由器。周末到了!

结论

好吧,我们终于结束了。在本文中,我们学习了如何PassportNode.js应用程序中使用实现本地身份验证在此过程中,我们还学习了如何MongoDB使用进行连接Mongoose

也许这对您来说不像我尝试绘制时那样容易,但是至少您看到了这些工具在后台运行时变得更容易了,让我们仅担心我们尝试构建的内容。

“魔术”工具并不总是理想的,但是信誉良好且维护良好的工具可以帮助我们减少编写代码的数量,并且您不编写的代码就是您不需要维护的代码,而您不维护的代码就是您不会破坏的代码。

另外,请记住,如果一个工具由核心团队积极维护,他们很可能知道自己在做什么方面比我们任何一个都要好。尽可能委托。

我希望您喜欢本教程,并且可能对您的下一个项目有所启发。祝您编码愉快!

飞码网-免费源码博客分享网站 爱上飞码网—https://www.codefrees.com— 飞码网-matlab-python-C++ 爱上飞码网—https://www.codefrees.com— 飞码网-免费源码博客分享网站
赞 ()
内容页底部广告位3
留言与评论(共有 0 条评论)
   
验证码: