javascript - 使用 Express 和 MongoDB - 如何注销用户?

标签 javascript node.js mongodb express jwt

我正在关注 Authentication in NodeJS With Express and Mongo - CodeLab #1 的教程

我让一切正常工作,但本教程没有解决如何注销用户。

据我所知, session 保存在 Mongoose Atlas 上,这是我正在使用的数据库。当我使用 Postman 登录用户时,我会得到一个 token 。但我不确定如何配置/logout 路由。

这是我的代码:

//routes/user.js
const express = require("express");
const { check, validationResult } = require("express-validator");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const router = express.Router();
const auth = require("../middleware/auth");

const User = require("../models/User");

/**
 * @method - POST
 * @param - /signup
 * @description - User SignUp
 */

//Signup
router.post(
  "/signup",
  [
    check("username", "Please Enter a Valid Username")
      .not()
      .isEmpty(),
    check("email", "Please enter a valid email").isEmail(),
    check("password", "Please enter a valid password").isLength({
      min: 6
    })
  ],
  async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({
        errors: errors.array()
      });
    }

    const {
      username,
      email,
      password
    } = req.body;
    try {
      let user = await User.findOne({
        email
      });
      if (user) {
        return res.status(400).json({
          msg: "User Already Exists"
        });
      }

      user = new User({
        username,
        email,
        password
      });

      const salt = await bcrypt.genSalt(10);
      user.password = await bcrypt.hash(password, salt);

      await user.save();

      const payload = {
        user: {
          id: user.id
        }
      };

      jwt.sign(
        payload,
        "randomString", {
        expiresIn: 10000
      },
        (err, token) => {
          if (err) throw err;
          res.status(200).json({
            token
          });
        }
      );
    } catch (err) {
      console.log(err.message);
      res.status(500).send("Error in Saving");
    }
  }
);

// Login
router.post(
  "/login",
  [
    check("email", "Please enter a valid email").isEmail(),
    check("password", "Please enter a valid password").isLength({
      min: 6
    })
  ],
  async (req, res) => {
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
      return res.status(400).json({
        errors: errors.array()
      });
    }

    const { email, password } = req.body;
    try {
      let user = await User.findOne({
        email
      });
      if (!user)
        return res.status(400).json({
          message: "User Not Exist"
        });

      const isMatch = await bcrypt.compare(password, user.password);
      if (!isMatch)
        return res.status(400).json({
          message: "Incorrect Password !"
        });

      const payload = {
        user: {
          id: user.id
        }
      };

      jwt.sign(
        payload,
        "randomString",
        {
          expiresIn: 3600
        },
        (err, token) => {
          if (err) throw err;
          res.status(200).json({
            token
          });
        }
      );
    } catch (e) {
      console.error(e);
      res.status(500).json({
        message: "Server Error"
      });
    }
  }
);

// router.route("/logout").get(function (req, res, next) {
//   if (expire(req.headers)) {
//     delete req.user;
//     return res.status(200).json({
//       "message": "User has been successfully logged out"
//     });
//   } else {
//     return next(new UnauthorizedAccessError("401"));
//   }
// });

router.get("/me", auth, async (req, res) => {
  try {
    // request.user is getting fetched from Middleware after token authentication
    const user = await User.findById(req.user.id);
    res.json(user);
  } catch (e) {
    res.send({ message: "Error in Fetching user" });
  }


});

router.get('/logout', isAuthenticated, function (req, res) {
  console.log('User Id', req.user._id);
  User.findByIdAndRemove(req.user._id, function (err) {
    if (err) res.send(err);
    res.json({ message: 'User Deleted!' });
  })
});


module.exports = router;

function isAuthenticated(req, res, next) {
  console.log("req: " + JSON.stringify(req.headers.authorization));
  // if (!(req.headers && req.headers.authorization)) {
  //   return res.status(400).send({ message: 'You did not provide a JSON web token in the authorization header' });
  //}
};
///middleware/auth.js
const jwt = require("jsonwebtoken");

module.exports = function (req, res, next) {
  const token = req.header("token");
  if (!token) return res.status(401).json({ message: "Auth Error" });

  try {
    const decoded = jwt.verify(token, "randomString");
    req.user = decoded.user;
    next();
  } catch (e) {
    console.error(e);
    res.status(500).send({ message: "Invalid Token" });
  }
};
///models/User.js
const mongoose = require("mongoose");

const UserSchema = mongoose.Schema({
  username: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true
  },
  password: {
    type: String,
    required: true
  },
  createdAt: {
    type: Date,
    default: Date.now()
  }
});

// export model user with UserSchema
module.exports = mongoose.model("user", UserSchema);

所以我的问题是,如何实现/logout 路由,以便如果用户单击注销按钮并调用该路由,他们的 token 将被销毁。我只询问后端部分。我可以处理使用 axios。

谢谢。

最佳答案

从我看来,您没有在任何地方保存任何 session 数据或存储 token - 这很好。您只需将 token 附加到 API 请求中的 header 即可。

所以你唯一能做的就是让 /logout route 中的 token 过期。
然后确保您删除客户端上的 token - 可能是 localStorage、sessionStorage 等 - 您的客户端代码需要终止该 token ,因此无法再次包含该 token 。

边注:

  • 您不会在任何地方延长 token 生命周期,因此即使用户继续在网站上进行交互, token 过期时间也不会更新。您将需要手动刷新 token /生成新 token 以具有滑动到期。
  • 我建议您将 token 保存在 cookie 中。将 cookie 设置为 HttpOnly、Secure,并指定域。这是更安全 并且将允许您也使来自 API 的 cookie 过期。如果您包含的任何脚本遭到入侵,他们可以轻松访问您所有用户的 token 。

  • 例子:
    import {serialize} from 'cookie';
    import jsend from 'jsend';
    
    ...
    const token = jwt.sign(
        {
            id: validationResult.value.id // whatever you want to add to the token, here it is the id of a user
        },
        privateKeyBuffer,
        {
            expiresIn: process.env.token_ttl,
            algorithm: 'RS256'
        });
    
    const cookieOptions = {
        httpOnly: true,
        path: '/',
        maxAge: process.env.token_ttl,
        expires: new Date(Date.now() + process.env.token_ttl),
        sameSite: process.env.cookie_samesite, // strict
        domain: process.env.cookie_domain, // your domain
        secure: process.env.cookie_secure // true
    };
    
    const tokenCookie = await serialize('token', token, cookieOptions);
    
    res.setHeader('Set-Cookie', [tokenCookie]);
    
    res.setHeader('Content-Type', 'application/json');
    res.status(200).json(jsend.success(true));
    

    然后在注销:
        // grab from req.cookies.token and validate
        const token = await extractToken(req);
    
        // you can take action if it's invalid, but not really important
        if(!token) {
           ...
        }
    
        // this is how we expire it - the options here must match the options you created with!
        const cookieOptions = {
            httpOnly: true,
            path: '/',
            maxAge: 0,
            expires: 0,
            sameSite: process.env.cookie_samesite, // strict
            domain: process.env.cookie_domain, // your domain
            secure: process.env.cookie_secure // true
        };
    
        // set to empty 
        const tokenCookie = await serialize('token', '', cookieOptions);
    
        res.setHeader('Set-Cookie', [tokenCookie]);
        res.setHeader('Content-Type', 'application/json');
        res.status(200).json(jsend.success(true));
    

    关于javascript - 使用 Express 和 MongoDB - 如何注销用户?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62270350/

    相关文章:

    javascript - 如何检查mongodb中特定字段的文档是否存在

    javascript - 将结果存储在变量中,就像 Java 在 TypeScript 中一样

    mongodb 通过 Haskell : How to enable text search

    javascript - 查找最接近的单选按钮组的值

    javascript - 隐藏/显示表格排序器的过滤器

    javascript - nodeJS和 Electron 之间的区别

    node.js - 重新启动后,nodejs 应用程序无法永久正确启动

    javascript - 无法连接到本地 mongoDB 安装

    javascript - 如何从 AJAX 调用中使用 jQuery 呈现 HTML

    javascript - 为什么使用 JS Promise 而不是 IF/ELSE/Ternary?