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

介绍

就像身份验证在API中很重要一样,它也是某些Web应用程序中的重要功能-那些页面和机密仅应由注册和身份验证的用户访问。

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

应用程式设定

在您将要工作的地方创建一个新目录。 为了本教程的缘故,我将其称为mine site-auth 。 在刚创建的新目录中初始化npm。 这是初始化npm的方法。

npm init -y

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

编辑package.json文件的依赖项部分,使其看起来像我的一样。

#package.json

{
  "name": "site-auth",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "izuchukwu1",
  "license": "ISC",
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "body-parser": "^1.17.1",
    "connect-flash": "^0.1.1",
    "cookie-parser": "^1.4.3",
    "express": "^4.15.2",
    "express-handlebars": "^3.0.0",
    "express-messages": "^1.0.1",
    "express-session": "^1.15.2",
    "joi": "^13.0.1",
    "mongoose": "^4.11.12",
    "morgan": "^1.8.1",
    "passport": "^0.4.0",
    "passport-local": "^1.0.0"
  }
}

完成后,运行命令以安装依赖项。

npm install

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

首先需要安装的依赖项和必要的文件。

#app.js

const express = require('express');
const morgan = require('morgan')
const path = require('path');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const expressHandlebars = require('express-handlebars');
const flash = require('connect-flash');
const session = require('express-session');
const mongoose = require('mongoose')
const passport = require('passport')

require('./config/passport')

这些依赖项是在您运行npm install时安装的。 要在您的应用程序中使用它们,您必须要求它们并将它们保存在各自的常量中。

对于本教程,您将使用MongoDB作为数据库。 您将需要在数据库中存储用户信息。 要使用MongoDB,您将使用Mongoose(一种用于Node.js的MongoDB建模工具)。 这样,设置猫鼬很容易。

#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({ defaultLayout: 'layout' }))
app.set('view engine', 'handlebars')

// 3
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.static(path.join(__dirname, 'public')))
app.use(session({
  cookie: { maxAge: 60000 },
  secret: 'codeworkrsecret',
  saveUninitialized: false,
  resave: false
}));

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

// 4
app.use(flash())
app.use((req, res, next) => {
  res.locals.success_mesages = req.flash('success')
  res.locals.error_messages = req.flash('error')
  next()
})

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

// 6
// catch 404 and forward to error handler
app.use((req, res, next) => {
  res.render('notFound')
});

// 7
app.listen(5000, () => console.log('Server started listening on port 5000!'))
  1. Express已初始化并分配给app
  2. 设置了用于处理视图的中间件。 对于视图,您将使用handlebars
  3. 您设置了用于bodyparsercookiesessionpassport中间件。 用户要登录时将使用Passport。
  4. 在某些时候,您将显示即时消息。 因此,您需要为此设置中间件,并创建所需的Flash消息类型。
  5. 路由中间件-这将处理对URL路径的任何请求。 此处指定的URL路径用于索引和用户路径。
  6. 处理404错误的中间件。 当请求未映射到在其上方创建的任何中间件时,该中间件即会启动。
  7. 服务器设置为侦听端口5000。

视图设置

创建一个名为views的新目录。 在views目录中,创建另外两个目录,称为layoutspartials 。 您希望在视图中实现这样的树结构,因此请在它们各自的目录中创建必要的文件。

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

完成后,是时候删除代码了。

#dashboard.handlebars

<!-- Jumbotron -->
<div class="jumbotron">
  <h1>User DashBoard</h1>
</div>

这是一个仪表板,仅对注册用户可见。 对于本教程,这将是您的秘密页面。

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

#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>
</div>

您的注册页面应该看起来像这样。

<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>

完成此操作后,您很高兴深入了解某些部分。

资料验证

您将需要一个用户模型。 从上面的视图代码中,您可以推断出用户模型所需的属性是电子邮件,用户名和密码。 创建一个名为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 = User
  1. 导入依赖项并将其保存为常量。
  2. 创建一个新的架构。 对于每个用户,您要将emailusernamepassword保存到数据库中。 该模式显示了如何为每个文档构造模型。 在这里,您希望电子邮件,用户名和密码为字符串类型。
  3. 对于保存到数据库的每个用户,您还希望创建timestamps 。 你利用猫鼬获得createdAtupdatedAt ,然后将此保存到数据库中。
  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)
  }
}

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

索引和用户路线

创建一个名为路由的新目录。 在这个新目录中,创建两个新文件: index.jsusers.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. 验证用户输入。 您要确保用户未提交空白表格。 所有输入都是必需的,并且所有输入都必须为String类型。 电子邮件具有称为.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 = Joi.validate(req.body, userSchema)
      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

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

可通过req.body访问在注册表中输入的值,这些值看起来像这样。

value: 
   { email: 'chineduizuchkwu1@gmail.com',
     username: 'izu',
     password: 'chinedu',
     confirmationPassword: 'chinedu' },

使用上面创建的userSchema对此进行验证,并将用户输入的值分配给一个名为result的常量。

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

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

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

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

result.value.password = hash

新的用户实例将保存到数据库。 将显示一条简短消息,指示注册成功,并且用户被重定向到登录页面。

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

node app.js

将浏览器指向http:// localhost:5000 ,您应该会看到新应用。

结论

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

接下来,您将看到如何为注册用户实施登录功能。 我相信你很开心!

翻译自: https://code.tutsplus.com/tutorials/site-authentication-in-nodejs-user-sign-up--cms-29933

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值