• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

Node.js 中的站点身份验证:用户注册和登录

正如身份验证在 api 中很重要一样,它也是某些 Web 应用程序中的一项重要功能——尤其是那些只有注册和经过身份验证的用户才能访问的页面和机密的应用程序。

在本教程中,您将构建一个简单的 Web 应用程序,同时学习如何创建用户注册。

应用程序设置

创建一个新目录,您将在其中工作。对于本教程,我们将创建一个名为site-auth的文件夹。您可以在刚刚创建的新目录中初始化 npm。导航到您目录的终端并通过键入下面的代码片段来初始化 npm。

npm init -y

该-y标志告诉 npm 使用默认选项。

我们还将为此项目安装一些依赖项。按如下方式安装它们:

npm install bcryptjs
npm install connect-flash
npm install cookie-parser
npm install express
npm install express-handlebars
npm install express-messages
npm install express-session
npm install joi
npm install mongoose
npm install morgan
npm install passport
npm install passport-local

现在在您的工作目录中创建一个名为app.js的文件。

首先需要您安装的依赖项和任何其他必要的文件。

// app.js
 
const express = require('express');
const morgan = require('morgan')
const path = require('path');
const cookieParser = require('cookie-parser');
const expressHandlebars = require('express-handlebars');
const flash = require('connect-flash');
const session = require('express-session');
const mongoose = require('mongoose')
const passport = require('passport')

对于本教程,您将使用 mongodb 作为数据库。您需要将用户信息存储在数据库中。要使用 MongoDB,您将使用 Mongoose——一个用于 node.js 的 MongoDB 建模工具。设置 Mongoose 很简单,就像这样。

// app.js
 
mongoose.Promise = global.Promise
mongoose.connect('mongodb://localhost:27017/site-auth'

此时,让我们设置我们的中间件。

// 1
const app = express()
app.use(morgan('dev'))
 
// 2
app.set('views', path.join(__dirname, 'views'))
app.engine('handlebars', expressHandlebars.engine({
    extname: '.handlebars',
    defaultLayout: 'layout',
    layoutsDir: "views/layouts/"
}));
app.set('view engine', 'handlebars')
 
// 3
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(cookieParser())
app.use(express.static(path.join(__dirname, 'public')))
app.use(session({
  cookie: { maxAge: 60000 },
  secret: 'cookiesecret',
  saveUninitialized: false,
  resave: false
}));
 
app.use(passport.initialize())
app.use(passport.session())
 
// 4
app.use(flash())
app.use((req, res, next) => {
  res.locals.success_messages = req.flash('success')
  res.locals.error_messages = req.flash('error')
  next()
})
 
 
// 5
// catch 404 and forward to error handler
app.use((req, res, next) => {
  res.render('notFound')
});
 
// 6
app.listen(5000, () => console.log('Server started listening on port 5000!')
  1. Express 被初始化并分配给app.

  2. 设置了处理视图的中间件。对于视图,您将使用handlebars.

  3. express urlencoded您为、cookie、session和设置中间件passport。当用户想要登录时,将使用 Passport。

  4. 在某些时候,您将显示 Flash 消息。因此,您需要为此设置中间件,并创建所需的 Flash 消息类型。

  5. 处理 404 错误的中间件。当请求没有映射到在它上面创建的任何中间件时,这个中间件就会启动。

  6. 服务器设置为***端口 5000。

我们可以使用以下命令在我们的计算机上运行我们刚刚创建的服务器:

node app.js

您应该看到消息服务器开始在端口 5000 上***!如果您的设置成功。

视图设置

创建一个名为views的新目录。在 views 目录中,创建另外两个名为layouts和partials的目录。您想在您的视图文件夹中实现这样的树结构,因此在它们各自的目录中创建必要的文件。


├── layouts
│   └── layout.handlebars├── partials
│   └── navbar.handlebars
├── dashboard.handlebars
├── index.handlebars
├── login.handlebars
├── notFound.handlebars
└── register.handlebars

Node.js 中的站点身份验证:用户注册和登录  第1张

项目文件结构

完成文件结构后,就该编写代码了。

#views/dashboard.handlebars
 
<!-- Jumbotron -->
<div class="jumbotron">
  <h1>User DashBoard</h1>
</div

这是一个只有注册用户才能看到的仪表板。对于本教程,这将是您的秘密页面。

现在应用程序的索引页面应该如下所示。

#views/index.handlebars
 
<!-- Jumbotron -->
<div class="jumbotron">
  <h1>Site Authentication!</h1>
  <p class="lead">Welcome aboard.</p>
</div

应用程序需要一个将要使用的布局,这是您将要使用的布局。

#layout/layout.handlebars
 
<!DOCTYPE html>
<html>
  <head>
    <title>Site Authentication</title>
    <link rel="stylesheet"
          href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
          crossorigin="anonymous">
    <link rel="stylesheet"
          href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"
          integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp"
          crossorigin="anonymous">
    <link rel="stylesheet"
          href="/css/style.css">
  </head>
  <body>
    {{#if success_messages }}
      <div class="alert alert-success">{{success_messages}}</div>
    {{/if}}
    {{#if error_messages }}
      <div class="alert alert-danger">{{error_messages}}</div>
    {{/if}}
    <div class="container">
      {{> navbar}}
      {{{body}}}
    </div>
 
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
            integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
            crossorigin="anonymous"></script>
  </body>
</html

您需要注册用户的登录页面。

#views/login.handlebars
 
<form class="form-signin" action="/users/login" method="post">
  <h2 class="form-signin-heading">Please sign in</h2>
 
  <label for="inputEmail" class="sr-only">Email address</label>
  <input type="email" id="inputEmail" name="email" class="form-control" placeholder="Email address" required autofocus>
 
  <label for="inputPassword" class="sr-only">Password</label>
  <input type="password" id="inputPassword" name="password" class="form-control" placeholder="Password" required>
 
  <br/>
   
  <button class="btn btn-lg btn-default btn-block" type="submit">Sign in</button>
</form

notFound .handlebars文件将用作您的错误页面。

#views/notFound.handlebars
 
<!-- Jumbotron -->
<div class="jumbotron">
  <h1>Error</h1>
</di

您的注册页面应该是这样的。

#views/register.handlebars
 
<form class="form-signin" action="/users/register" method="POST">
  <h2 class="form-signin-heading">Please sign up</h2>
 
  <label for="inputEmail" class="sr-only">Email address</label>
  <input type="email" id="inputEmail" name="email" class="form-control" placeholder="Email address" required autofocus>
 
  <label for="inputUsername" class="sr-only">Username</label>
  <input type="text" id="inputUsername" name="username" class="form-control" placeholder="Username" required>
 
  <label for="inputPassword" class="sr-only">Password</label>
  <input type="password" id="inputPassword" name="password" class="form-control" placeholder="Password" required>
 
  <label for="inputconfirmPassword" class="sr-only">Confirm Password</label>
  <input type="password" id="inputConfirmPassword" name="confirmationPassword" class="form-control" placeholder="Confirm Password" required>
 
  <br/>
 
  <button class="btn btn-lg btn-default btn-block" type="submit">Sign up</button>
</form

最后为了您的意见,这里是您的导航栏。

#partials/navbar.handlebars
 
<div class="masthead">
  <h3 class="text-muted">Site Authentication</h3>
  <nav>
    <ul class="nav nav-justified">
      <li class="active"><a href="/">Home</a></li>
      {{#if isAuthenticated}}
        <li><a href="/users/dashboard">Dashboard</a></li>
        <li><a href="/users/logout">Logout</a></li>
      {{else}}
        <li><a href="/users/register">Sign Up</a></li>
        <li><a href="/users/login">Sign In</a></li>
      {{/if}}
    </ul>
  </nav>
</div

完成后,你就可以进入一些更深的部分了。

数据验证

您将需要一个用户模型。从上面的视图代码中,您可以推断出 User 模型所需的属性是电子邮件、用户名和密码。创建一个名为models的目录,并在其中创建一个名为user.js的文件。

#models/user.js
 
// 1
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const bcrypt = require('bcryptjs')
 
// 2
const userSchema = new Schema({
  email: String,
  username: String,
  password: String
}, {
 
  // 3
  timestamps: {
    createdAt: 'createdAt',
    updatedAt: 'updatedAt'
  }
})
 
// 4
const User = mongoose.model('user', userSchema)
module.exports = Use
  1. 导入依赖项并将它们保存在常量中。

  2. 创建一个新的模式。对于每个用户,您都希望将 、 和 保存email到username数据库password中。Schema 显示了如何为每个文档构建模型。在这里,您希望电子邮件、用户名和密码为字符串类型。

  3. 对于保存到数据库的每个用户,您还需要创建timestamps. 您利用 Mongoose 获取createdatand updatedAt,然后将其保存到数据库中。

  4. 该模型被定义并分配给一个名为 的常量User,然后将其导出为一个模块,以便可以在应用程序的其他部分中使用它。

密码的加盐和散列

您不想将用户的密码存储为纯文本。当用户在注册时输入纯文本密码时,您需要执行以下操作。应该使用将由您的应用程序生成的盐(使用 bcryptjs)对纯文本密码进行哈希处理。然后将此散列密码存储在数据库中。

听起来不错,对吧?让我们在user.js文件中实现它。

#models/user.js
 
module.exports.hashPassword = async (password) => {
  try {
    const salt = await bcrypt.genSalt(10)
    return await bcrypt.hash(password, salt)
  } catch(error) {
    throw new Error('Hashing failed', error)
  }

您刚刚创建了一个将在用户注册事件中调用的方法。该方法将接收用户输入的纯文本密码。正如我之前提到的,纯文本密码将使用生成的盐进行哈希处理。散列后的密码将作为用户的密码返回。

索引和用户路由

创建一个名为routes的新目录。在这个新目录中,创建两个新文件:index.js和users.js。

index.js文件将非常简单。它将映射到您的应用程序的索引。请记住,当您执行此操作时,您在app.js文件中为您的路由设置了中间件。

app.use('/', require('./routes/index'))
app.use('/users', require('./routes/users')

因此,您的索引路由(仅呈现索引页面)应该如下所示。

#routes/index.js
 
const express = require('express')
const router = express.Router()
 
router.get('/', (req, res) => {
    res.render('index')
})
 
module.exports = router

注册实施 

现在到用户路线。目前,这个路由文件将做四件事。

  1. 需要依赖。您将需要使用 NPM 安装的依赖项。

  2. 验证用户输入。您要确保用户不提交空表单。所有输入都是必需的,并且都必须是字符串类型。电子邮件有一个称为特殊验证的.email()方法,可确保输入的内容与电子邮件格式匹配,同时使用正则表达式验证密码。对于确认密码,您希望它与输入的密码相同。这些验证是使用Joi完成的。

  3. 设置你的路由器。GET请求呈现注册页面,而POST请求在用户点击按钮提交表单时启动。

  4. 路由器作为模块导出。

这是代码的样子。

#routes/users.js
 
const express = require('express');
const router = express.Router()
const Joi = require('joi')
const passport = require('passport')
 
const User = require('../models/user')
 
 
//validation schema
 
const userSchema = Joi.object().keys({
  email: Joi.string().email().required(),
  username: Joi.string().required(),
  password: Joi.string().regex(/^[a-zA-Z0-9]{6,30}$/).required(),
  confirmationPassword: Joi.any().valid(Joi.ref('password')).required()
})
 
router.route('/register')
  .get((req, res) => {
    res.render('register')
  })
  .post(async (req, res, next) => {
    try {
      const result = userSchema.validate(req.body)
      if (result.error) {
        req.flash('error', 'Data entered is not valid. Please try again.')
        res.redirect('/users/register')
        return
      }
 
      const user = await User.findOne({ 'email': result.value.email })
      if (user) {
        req.flash('error', 'Email is already in use.')
        res.redirect('/users/register')
        return
      }
 
      const hash = await User.hashPassword(result.value.password)
 
      delete result.value.confirmationPassword
      result.value.password = hash
 
      const newUser = await new User(result.value)
      await newUser.save()
 
      req.flash('success', 'Registration successfully, go ahead and login.')
      res.redirect('/users/login')
 
    } catch(error) {
      next(error)
    }
  })
 
  module.exports = router

密码的正则表达式格式/^[a-zA-Z0-9]{6,30}$/——表示密码应包含小写或大写字母或数字,密码长度应最少为 6 个字符,最多为 30 个字符。 

让我们更深入地了解该POST请求中发生的情况。

在注册表单中输入的值可通过 访问req.body,值如下所示。

{
    email: 'maryokosun@gmail.com',
    username: 'marynoir',
    password: 'marynoir',
    confirmationPassword: 'marynoir'

这是使用userSchema您在上面创建的验证的,并且用户输入的值被分配给一个名为 result 的常量。

如果由于验证而遇到错误,则会向用户显示错误消息并重定向到注册页面。

否则,我们会尝试查找是否存在具有相同电子邮件地址的用户,因为您不希望两个或更多用户具有相同的电子邮件地址。如果找到用户,则告知用户该电子邮件地址已在使用中。

在没有注册用户拥有该电子邮件地址的情况下,下一步是对密码进行哈希处理。这是您调用hashPassword在 user.js 文件中创建的方法的地方。新的散列密码被分配给一个称为散列的常量。

无需将其存储confirmationPassword在数据库中。因此将其删除。结果中可用的密码仍然是普通密码。由于您不想将纯密码存储在数据库中,因此将密码值重新分配给已创建的哈希非常重要。这是通过一行代码完成的。

result.value.password = hash

新的用户实例被保存到数据库中。将显示一条表明注册成功的闪烁消息,并将用户重定向到登录页面。

通过运行以下命令从终端启动服务器:

node app.js

将浏览器指向https://localhost:5000,您应该会看到应用程序的注册页面。

Node.js 中的站点身份验证:用户注册和登录  第2张
注册页面

登录实施 

户成功注册后,您将被引导到登录页面。接下来的步骤将是实现登录功能的代码。在/routes/users.js文件中,在验证模式中,就在 userSchema 下方,为登录验证模式添加以下代码片段

const loginSchema = Joi.object().keys({
  email: Joi.string().email().required(),
  password: Joi.string().regex(/^[a-zA-Z0-9]{6,30}$/).required()
}

在路由部分,在注册路由下方添加以下代码片段

router.route('/login')
  .get((req, res) => {
    res.render('login')
  })
  .post(async (req, res, next) => {
  try {
    const result = loginSchema.validate(req.body)
    if (result.error) {
      req.flash('error', 'Data entered is not valid. Please try again.')
      res.redirect('/users/login')
      return
    }
 
    const user = await User.findOne({ 'email': result.value.email })
    if (user) {
      let passwordIsValid = bcrypt.compareSync(
        result.value.password,
        user.password
      );
      if (!passwordIsValid) {
        req.flash('error', 'Email/Password is not valid. Please try again.')
        res.redirect('/users/login')
        return
      }
      req.flash('success', 'Login successfully')
      res.redirect('/users/dashboard')
    }
  } catch (error) {
    next(error)
  }
}

上面的代码片段设置了登录路由器。GET请求呈现登录页面,而POST请求处理验证req.body 并将密码与数据库中保存的密码进行比较。如果这些检查成功,则注册用户可以成功登录并被路由到仪表板页面。

Node.js 中的站点身份验证:用户注册和登录  第3张

仪表板实施 

仪表板的实现非常简单。在索引页面的GET请求下方的/routes.index.js文件中添加以下代码片段。

router.get('/users/dashboard', (req, res) => {    res.render('dashboard')})

当向/users/dashboard发出GET请求时,上面的代码会呈现仪表板页面。


Node.js 中的站点身份验证:用户注册和登录  第4张
仪表板


结论

现在您知道了如何  在 Node Web 应用程序中使用 node.js 实现注册和登录系统。您已经了解了验证用户输入的重要性以及如何使用 Joi 进行验证。您还使用了bcryptjs对您的密码进行加盐和哈希处理。

您可以在GitHub 存储库中找到该示例的完整源代码。

文章目录
  • 应用程序设置
  • 视图设置
  • 数据验证
  • 密码的加盐和散列
  • 索引和用户路由
    • 注册实施
    • 登录实施
    • 仪表板实施
  • 结论
  • 发表评论