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

hc2pp10m  于 2023-08-04  发布在  Go
关注(0)|答案(3)|浏览(115)

我在Authentication in NodeJS With Express and Mongo - CodeLab #1上学习教程
我得到了一切完美的工作,但教程并没有解决如何注销用户。
据我所知,该会话正在保存在Mongoose Atlas上,这是我正在使用的数据库。当我用Postman登录用户时,我会得到一个令牌。但我不确定如何配置/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路由,以便在用户单击logout按钮并调用该路由时,销毁他们的令牌。我只是问后端的部分。我可以使用axios。

  • 谢谢-谢谢
xdnvmnnf

xdnvmnnf1#

从我所看到的,你没有保存任何会话数据或存储令牌的任何地方-这是伟大的。您只需将令牌附加到API请求中的标头。
因此,您唯一可以做的事情可能是使/logout route中的令牌过期,然后确保删除客户端上的令牌-可以是localStorage,sessionStorage等-您的客户端代码需要杀死令牌,以便它不能再次包含。
旁注:
1.您不会在任何地方延长令牌的生命周期,因此即使用户继续在网站上进行交互,令牌的到期时间也不会更新。您将需要手动刷新令牌/生成新令牌以具有滑动到期时间。
1.我建议你把代币保存在饼干里。将cookie设置为HttpOnly、Secure并指定域。这是更安全,并允许您也从API过期的cookie。如果您包含的任何脚本遭到破坏,它们可以轻松访问所有用户的令牌。
示例如下:

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

6jjcrrmo

6jjcrrmo2#

当你使用JWT时,后端总是会检查2件事1.正确的令牌2.如果时间到了(你应该处理这个)
对于第二点,如果用户时间超过前端,如果您已将令牌存储在本地存储中,则可以删除令牌。
对于注销,当用户单击注销时,只需从本地存储中删除JWT并重定向到登录或其他页面

gkn4icbw

gkn4icbw3#

这是一个简单的解决方案

const Logout = async (req, res, next) => {
res.cookie('cookie', '', {
    expireIn: new Date(Date.now())
})
return res.status(200).json({ message: 'User Logout Successfully' })

字符串
}
注销用户的简单方法

相关问题