IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Nodejs - 9步开启JWT身份验证

    南城FE发表于 2024-02-02 09:46:08
    love 0
    本文翻译自 9 Steps for JWT Authentication in Node.js Application,作者:Shefali, 略有删改。

    身份验证是Web开发的重要组成部分。JSON Web令牌(JWT)由于其简单性,安全性和可扩展性,已成为在Web应用程序中实现身份验证的流行方法。在这篇文章中,我将指导你在Node.js应用程序中使用MongoDB进行数据存储来实现JWT身份验证。

    在开始之前,我假设你已经安装了Node.js、MongoDB和VS Code,并且你知道如何创建MongoDB数据库和基本的RESTful API。

    什么是JWT认证?

    JWT身份验证依赖于JSON Web令牌来确认Web应用中用户的身份。JSON Web令牌是使用密钥对进行数字签名的编码JSON对象。

    简而言之,JWT身份验证就像为网站提供一个密码。一旦你登录成功,你就得到了这个密码。

    JSON Web Token由三部分组成,由点(.)分隔:

    • Header
    • Payload
    • Signature

    以下是JWT的基本结构:

    xxxx.yyyy.zzzz
    • Header:这部分包含有关令牌的信息,如其类型和如何保护。
    • Payload:这部分包含关于用户的声明,如用户名或角色。
    • Signature:确保令牌的完整性,并验证它没有被更改,这可以确保代码安全,不会被篡改。

    当你登录成功时,你会得到这个代码。每次你想访问某个数据时,你都要携带这个代码来证明是你。系统会检查代码是否有效,然后让你获取数据!

    接下来让我们看看在node.js项目中进行JWT身份验证的步骤。

    步骤1:新建项目

    首先为您的项目创建一个新目录,并使用以下命令进入到该目录。

    mkdir nodejs-jwt-auth
    cd nodejs-jwt-auth

    通过在终端中运行以下命令初始化项目(确保您位于新创建的项目文件夹中)。

    npm init -y

    接下来通过以下命令安装必要的依赖项:

    npm install express mongoose jsonwebtoken dotenv

    上面的命令将安装:

    • express: 用于构建Web服务器。
    • mongoose:MongoDB的数据库。
    • jsonwebtoken:用于生成和验证JSON Web令牌(JWT)以进行身份验证。
    • dotenv:用于从.env文件加载环境变量。

    现在您的package.json文件应该看起来像这样:

    步骤2:连接MongoDB数据库

    要连接MongoDB数据库,请查看以下链接中的具体操作流程。

    https://shefali.dev/restful-api/#Step_4_Creating_a_MongoDB_Database

    步骤3:创建 .env 文件

    为了 MongoDB 连接地址的安全,让我们在根目录下创建一个名为 .env 的新文件。

    将以下代码添加到.env文件中。

    MONGODB_URL=<Your MongoDB Connection String>
    SECRET_KEY="your_secret_key_here"

    将<Your MongoDB Connection String>替换为您从MongoDB Atlas获得的连接字符串(在步骤2中),并将your_secret_key_here替换为您想要的密钥字符串。现在你的.env文件应该是这样的。

    MONGODB_URL='mongodb+srv://shefali:********@cluster0.sscvg.mongodb.net/nodejs-jwt-auth'
    SECRET_KEY="ThisIsMySecretKey"

    在MONGODB_URL最后我们加入node.js-jwt-auth,这是我们的数据库名称。

    步骤4:Express

    在根目录下创建一个名为index.js的文件,并将以下代码添加到该文件中。

    const express = require("express");
    const mongoose = require("mongoose");
    
    require("dotenv").config(); //for using variables from .env file.
    
    const app = express();
    const port = 3000;
    
    //middleware provided by Express to parse incoming JSON requests.
    app.use(express.json()); 
    
    mongoose.connect(process.env.MONGODB_URL).then(() => {
      console.log("MongoDB is connected!");
    });
    
    app.get("/", (req, res) => {
      res.send("Hello World!");
    });
    
    app.listen(port, () => {
      console.log(`Server is listening on port ${port}`);
    });

    现在我们可以通过以下命令运行服务器。

    node index.js

    输出应如下图所示。

    通过使用命令node index.js,您必须在每次更改文件时重新启动服务器。为了避免这种情况,您可以使用以下命令安装nodemon。

    npm install -g nodemon

    现在使用下面的命令运行服务器,它会在每次更改文件时自动重新启动服务器。

    nodemon index.js

    步骤5:创建用户数据库模型

    在根目录下创建一个名为models的新目录,并在其中创建一个名为User.js的新文件。

    现在让我们为我们的项目创建一个简单的模型,将以下代码添加到User.js文件中。

    const mongoose = require("mongoose");
    
    const userSchema = new mongoose.Schema({
      username: {
        type: String,
        required: true,
        unique: true,
      },
      password: {
        type: String,
        required: true,
      },
    });
    
    module.exports = mongoose.model("User", userSchema);

    步骤6:实现身份验证路由

    在根目录中,创建一个名为routes的新目录,并在其中创建一个名为auth.js的文件。

    然后将以下代码添加到该文件中:

    const express = require("express");
    const jwt = require("jsonwebtoken");
    const User = require("../models/User");
    const router = express.Router();
    
    // Signup route
    router.post("/signup", async (req, res) => {
      try {
        const { username, password } = req.body;
        const user = new User({ username, password });
        await user.save();
        res.status(201).json({ message: "New user registered successfully" });
      } catch (error) {
        res.status(500).json({ message: "Internal server error" });
      }
    });
    
    // Login route
    router.post("/login", async (req, res) => {
      const { username, password } = req.body;
      try {
        const user = await User.findOne({ username });
    
        if (!user) {
          return res.status(401).json({ message: "Invalid username or password" });
        }
        if (user.password !== password) {
          return res.status(401).json({ message: 'Invalid username or password' });
        }
        // Generate JWT token
        const token = jwt.sign(
          { id: user._id, username: user.username },
          process.env.SECRET_KEY
        );
        res.json({ token });
      } catch (error) {
        res.status(500).json({ message: "Internal server error" });
      }
    });
    
    module.exports = router;

    分解上面的代码:

    导入依赖:

    const express = require("express");
    const jwt = require("jsonwebtoken");
    const User = require("../models/User");
    const router = express.Router();

    在这里,我们导入以下依赖项:

    • express: 用于构建Web服务器。
    • jsonwebtoken:用于生成和验证JSON Web令牌(JWT)以进行身份验证。
    • User:从第5步中创建的User模块导入的模型。
    • router:Express中的Router()函数用于单独定义路由,然后将其合并到主应用程序中。

    注册路由:

    // Signup route
    router.post("/signup", async (req, res) => {
      try {
        const { username, password } = req.body;
        const user = new User({ username, password });
        await user.save();
        res.status(201).json({ message: "New user registered successfully" });
      } catch (error) {
        res.status(500).json({ message: "Internal server error" });
      }
    });
    • 此路由监听对/signup的POST请求。
    • 当接收到请求时,它从请求体中提取username和password。
    • 然后使用提供的用户名和密码创建User模型的一个新实例。
    • 调用save()方法将新用户保存到数据库。
    • 如果用户成功保存,它会返回一个状态码201和一个JSON消息,表示“新用户注册成功”。
    • 如果在此过程中发生错误,它会捕获错误并以状态代码500和错误消息“内部服务器错误”进行响应。

    登录路由:

    // Login route
    router.post("/login", async (req, res) => {
      const { username, password } = req.body;
      try {
        const user = await User.findOne({ username });
    
        if (!user) {
          return res.status(401).json({ message: "Invalid username or password" });
        }
        if (user.password !== password) {
          return res.status(401).json({ message: 'Invalid username or password' });
        }
        // Generate JWT token
        const token = jwt.sign(
          { id: user._id, username: user.username },
          process.env.SECRET_KEY
        );
        res.json({ token });
      } catch (error) {
        res.status(500).json({ message: "Internal server error" });
      }
    });
    • 此路由监听对/login的POST请求。
    • 当接收到请求时,它从请求体中提取username和password。
    • 然后在数据库中使用提供的username搜索用户。
    • 如果没有找到用户,它会返回一个状态码401(未经授权)和一个JSON消息,指示用户名或密码无效。
    • 如果找到用户,它会检查提供的password是否与数据库中存储的密码匹配。
    • 如果密码不匹配,它会返回一个状态码401(未经授权)和一个JSON消息,指示用户名或密码无效。
    • 如果密码匹配,它将使用jwt.sign()生成一个JWT。
    • 生成的令牌然后作为JSON响应发送。
    • 如果在此过程中出现错误,它会捕获错误并以状态代码500和错误消息“内部服务器错误”进行响应。

    最后路由被导出以在index.js文件中使用。

    module.exports = router;

    步骤7:使用中间件保护路由

    在根目录中,创建一个名为middleware.js的新文件,并将以下代码添加到该文件中。

    const jwt = require("jsonwebtoken");
    
    function verifyJWT(req, res, next) {
      const token = req.headers["authorization"];
    
      if (!token) {
        return res.status(401).json({ message: "Access denied" });
      }
    
      jwt.verify(token, process.env.SECRET_KEY, (err, data) => {
        if (err) {
          return res.status(401).json({ message: "Failed to authenticate token" });
        }
        req.user = data;
        next();
      });
    }
    
    module.exports = verifyJWT;

    此代码是一个中间件函数,用于在应用程序中验证JSON Web令牌(JWT)。

    分解上面的代码:

    • 在第一行中,我们导入jsonwebtoken库。
    • 然后定义verifyJWT中间件函数,它有三个参数:req(请求对象)、res(响应对象)和next(下一个中间件函数)。
    • 在中间件函数内部,它首先从请求头中提取token令牌。
    • 如果请求头中没有令牌,它将返回401(未经授权)状态沿着JSON响应,指示“拒绝访问”。
    • 如果存在令牌,它会尝试使用jwt.verify()进行验证。如果验证失败,它会捕获错误并返回一个401状态,其中包含一个JSON响应,指示“Failed to authenticate token”。
    • 如果令牌被成功验证,它将解码的令牌数据附加到req.user对象。
    • 最后导出verifyJWT函数,以便它可以用作应用程序其他部分的中间件。

    第8步:验证JWT

    现在要验证JWT,请修改index.js,如下所示:

    const express = require('express');
    const authRouter = require('./routes/auth');
    const mongoose = require("mongoose");
    const verifyJWT = require("./middleware")
    
    require("dotenv").config(); //for using variables from .env file.
    
    const app = express();
    const PORT = 3000;
    
    mongoose.connect(process.env.MONGODB_URL).then(() => {
        console.log("MongoDB is connected!");
    });
    app.use(express.json());
    
    //Authentication route
    app.use('/auth', authRouter);
    
    //decodeDetails Route
    app.get('/decodeDetails', verifyJWT, (req, res) => {
      const { username } = req.user;
      res.json({ username });
    });
    
    app.listen(PORT, () => {
      console.log(`Server is running on port ${PORT}`);
    });

    在上面的代码中,/auth路由是authRouter处理,其中包含的终端用户认证,例如登录和注册。

    app.get('/decodeDetails', verifyJWT, (req, res) => {
      const { username } = req.user;
      res.json({ username });
    });
    • 当向/decodeDetails发出请求时,verifyJWT中间件验证附加到请求的JWT令牌。
    • 如果令牌有效,则中间件从req.user中存储的解码令牌数据中提取username。
    • 最后路由处理程序发送一个JSON响应,其中包含从令牌中提取的username。

    步骤9:测试API

    注册

    向http://localhost:3000/auth/signup发送一个POST请求,其中包含Headers Content-Type : application/json和以下JSON主体:

    {
        "username": "shefali",
        "password": "12345678"
    }

    在响应中,您将看到消息“新用户注册成功”。

    登录

    向http://localhost:3000/auth/login发送一个POST请求,其中包含Header Content-Type : application/json和JSON主体以及用户名和密码,这是您在注册路由中创建的。

    {
        "username": "shefali",
        "password": "12345678"
    }

    在响应中,您将收到一个令牌。记下这个令牌,因为在测试decodeDetails路由时需要它。

    decodeDetails

    向http://localhost:3000/decodeDetails发送一个GET请求,并带有一个带有令牌值的Authorization头(您在测试登录路由时得到了它)。

    在响应中,您将获得用户名。恭喜你!🎉

    您已经在Node.js应用程序中成功实现了JWT身份验证。这种方法提供了一种安全有效的方式来验证Web应用程序中的用户。


    看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~

    专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)



沪ICP备19023445号-2号
友情链接