如何在Node.js应用程序中使用最新的JavaScript功能

如何在Node.js应用程序中使用最新的JavaScript功能

2019-09-13 21:12:34发布 浏览数:2339
概述:JavaScript的变化比以往任何时候都快。这意味着现有应用将快速使用过时的JavaScript功能。这也意味着您将无法使用JavaScript的最新功能,例如导入和导出模块,异步和等待,生成器以及许多可能派上用场的新功能。从版本10开始,Node.js运行时没有JavaScript的所有最新功能。默认情况下,它缺少对ES6模块的支持,这是一个很大的缺失功能。它将在未来启用。



在本文中,我们将构建一个使用JWT的应用程序,使用JavaScript的最新功能来存储身份验证数据。我们可以通过使用@babel-registerbabel-polyfill包来轻松使用它。


对于后端,我们将使用在Node.js上运行的Express框架,对于前端,我们将使用Angular框架。两者都有自己的JWT附加组件。在后端,我们有jsonwebtoken用于生成和验证令牌的包。


在前端,我们有@auth0/angular-jwtAngular 的模块。在我们的应用程序中,当用户输入用户名和密码并且他们在我们的数据库中时,JWT将从我们的密钥生成,返回给用户,并存储在本地存储中的前端应用程序中。每当用户需要访问后端的经过身份验证的路由时,他们都需要令牌。


在后端应用程序中将有一个称为中间件的功能来检查有效令牌。有效令牌是未过期的令牌,并根据我们的密钥验证为有效。除登录页面外,还会有注册和用户凭据设置页面。


现在我们可以开始构建应用程序了。首先,我们创建前端和后端应用程序文件夹。每人一个。

然后我们开始编写后端应用程序。首先,我们安装一些软件包并生成Express框架代码。我们运行npx express-generator生成代码。然后我们必须安装一些包。我们通过运行npm i @babel/register express-jwt sequelize bcrypt sequelize-cli dotenv jsonwebtoken body-parser cors@babel/register允许我们使用最新的JavaScript功能。


express-jwt生成JWT并根据密钥对其进行验证。bcrypt对我们的密码进行散列和腌制。sequelize是我们做CRUD的ORM。cors允许我们的Angular应用程序通过允许跨域通信与我们的后端进行通信。dotenv允许我们将环境变量存储在.env文件中。body-parserExpress需要解析JSON请求。

然后我们进行数据库迁移。首先,我们运行npx sequelize-cli init生成数据库到对象映射的框架代码。然后我们运行:

npx sequelize-cli model:generate --name User --attributes username:string, password:string, email:string


我们进行另一次迁移并放置:


'use strict';

module.exports = {
  up: (queryInterface, Sequelize) => {
    return Promise.all([
      queryInterface.addConstraint(
        "Users",
        ["email"],
        {
          type: "unique",
          name: 'emailUnique'
        }),queryInterface.addConstraint(
        "Users",
        ["userName"],
        {
          type: "unique",
          name: 'userNameUnique'
        }),
  },
  down: (queryInterface, Sequelize) => {
    return Promise.all([
      queryInterface.removeConstraint(
        "Users",
        'emailUnique'
      ),queryInterface.removeConstraint(
        "Users",
        'userNameUnique'
      ),
    ])
  }
};



这可以确保我们没有两个具有相同用户名或电子邮件的条目。

这将创建User模型,并在运行后创建Users表npx sequelize-cli db:migrate

然后我们写一些代码。首先,我们将以下内容放入app.js


require("@babel/register");
require("babel-polyfill");
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const user = require('./controllers/userController');
const app = express();app.use(cors())
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use((req, res, next) => {
  res.locals.session = req.session;
  next();
});
app.use('/user', user);app.get('*', (req, res) => {
  res.redirect('/home');
});
app.listen((process.env.PORT || 8080), () => {
  console.log('App running on port 8080!');
});


我们需要:


require("@babel/register");
require("babel-polyfill");


使用JavaScript中的最新功能。我们不能在此文件中使用Babel功能,但我们可以在此文件引用的其他文件中以及引用这些文件的任何文件中使用它。

我们需要:


require('dotenv').config();


.env文件中读取我们的配置。

这是切入点。我们很快就会userControllercontrollers文件夹中创建。

app.use(‘/user’, user);将任何以URL开头的URL路由user到该userController文件。

接下来,我们添加userController.js文件:


const express = require('express');
const bcrypt = require('bcrypt');
const router = express.Router();
const models = require('../models');
const jwt = require('jsonwebtoken');
import { saltRounds } from '../exports';
import { authCheck } from '../middlewares/authCheck';
router.post('/login', async (req, res) => {
    const secret = process.env.JWT_SECRET;
    const userName = req.body.userName;
    const password = req.body.password;
    if (!userName || !password) {
        return res.send({
            error: 'User name and password required'
        })
    }
    const users = await models.User.findAll({
        where: {
            userName
        }
    })    
    const user = users[0];
    if (!user) {
        res.status(401);
        return res.send({
            error: 'Invalid username or password'
        });
    }    
    try {
        const compareRes = await bcrypt.compare(password, user.hashedPassword);
        if (compareRes) {
            const token = jwt.sign(
                {
                    data: {
                        userName,
                        userId: user.id
                    }
                },
                secret,
                { expiresIn: 60 * 60 }
            );
            return res.send({ token });
        }
        else {
            res.status(401);
            return res.send({
                error: 'Invalid username or password'
            });
        }
    }
    catch (ex) {
        logger.error(ex);
        res.status(401);
        return res.send({
            error: 'Invalid username or password'
        });
    }});
router.post('/signup', async (req, res) => {
    const userName = req.body.userName;
    const email = req.body.email;
    const password = req.body.password;
    try {
        const hashedPassword = await bcrypt.hash(password, saltRounds)
        await models.User.create({
            userName,
            email,
            hashedPassword
        })
        return res.send({ message: 'User created' });
    }
    catch (ex) {
        logger.error(ex);
        res.status(400);
        return res.send({ error: ex });
    }
});
router.put('/updateUser', authCheck, async (req, res) => {
    const userName = req.body.userName;
    const email = req.body.email;
    const token = req.headers.authorization;
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    const userId = decoded.data.userId;
    try {
        await models.User.update({
            userName,
            email
        }, {
                where: {
                    id: userId
                }
            })
        return res.send({ message: 'User created' });
    }
    catch (ex) {
        logger.error(ex);
        res.status(400);
        return res.send({ error: ex });
    }});router.put('/updatePassword', authCheck, async (req, res) => {
    const token = req.headers.authorization;
    const password = req.body.password;
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    const userId = decoded.data.userId;
    try {
        const hashedPassword = await bcrypt.hash(password, saltRounds)
        await models.User.update({
            hashedPassword
        }, {
                where: {
                    id: userId
                }
            })
        return res.send({ message: 'User created' });
    }
    catch (ex) {
        logger.error(ex);
        res.status(400);
        return res.send({ error: ex });
    }});
module.exports = router;


login路线搜索用户条目。如果找到,则检查具有compare 函数的散列密码bcrypt。如果两者都成功,则生成JWT。该signup路由获取用户名和密码的JSON有效负载并保存。

请注意,保存前密码有哈希和腌制。密码不应存储为纯文本。

第一个是纯文本密码,第二个是一些盐轮。

updatePasswordroute是经过身份验证的路由。它检查令牌,如果它有效,它将继续通过User从解码的令牌中搜索带有用户ID 的用户密码来保存用户的密码。

authCheck接下来我们将添加中间件。我们创建一个middlewares文件夹并authCheck.js在其中创建。


const jwt = require('jsonwebtoken');
const secret = process.env.JWT_SECRET;

export const authCheck = (req, res, next) => {
    if (req.headers.authorization) {
        const token = req.headers.authorization;
        jwt.verify(token, secret, (err, decoded) => {
            if (err) {
                res.send(401);
            }
            else {
                next();
            }
        });
    }
    else {
        res.send(401);
    }
}


您应该使用相同的方法process.env.JWT_SECRET来生成和验证令牌。否则,验证将失败。秘密不应该在任何地方共享,不应该签入版本控制。

这允许我们在不重复代码的情况下检查已验证路由中的身份验证。我们通过导入和引用它将它放在每个经过身份验证的路由中的URL和主路由代码之间。

我们.env使用以下内容创建后端应用程序文件夹根目录的文件。(不应将其签入版本控制。)


DB_HOST='localhost'
DB_NAME='login-app'
DB_USERNAME='db-username'
DB_PASSWORD='db-password'
JWT_SECRET='secret'


后端应用程序现已完成。现在,我们可以使用前端应用程序,移动应用程序或任何HTTP客户端登录。



请先
登录
后评论
0 条评论
暂时没有评论
最新文章
更多