使用 Express 和 MongoDB - 如何注销用户?
Posted
技术标签:
【中文标题】使用 Express 和 MongoDB - 如何注销用户?【英文标题】:Using Express and MongoDB - How do I log out a User? 【发布时间】:2020-09-27 21:46:33 【问题描述】:我正在关注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 路由,以便如果用户单击注销按钮并调用该路由,他们的令牌会被销毁。我只问后端部分。我可以使用 axios 处理。
谢谢。
【问题讨论】:
【参考方案1】:当您使用 JWT 时,后端将始终检查 2 件事 1.适当的令牌 2.如果那个特定的时间结束了(你应该处理这个)
对于第二点,如果用户时间比从前端开始,如果您已将令牌存储在本地存储中,您可以删除令牌。
对于用户点击注销时的注销,只需从本地存储中删除 jwt 并重定向到登录或其他页面
【讨论】:
我没有将令牌存储在任何地方。也许这就是问题所在。我假设令牌是由 Mongoose 从 /login 路由中存储的。现在我更仔细地查看了该代码,看起来令牌刚刚生成,但我仍然需要存储它。我有这个权利吗? /me 路由从哪里得到它的信息? /me 路由根据令牌返回登录用户。 @David.Warwick 您可以将令牌保存在 req.header.authorization 中,并通过解密用户令牌来获取用户信息(您可以将用户信息保存在令牌中)【参考方案2】:据我所知,您没有在任何地方保存任何会话数据或存储令牌 - 这很好。您只是将令牌附加到 API 请求的标头中。
所以你唯一能做的就是让/logout route
中的令牌过期
然后确保您删除客户端上的令牌 - 可能是 localStorage、sessionStorage 等 - 您的客户端代码需要终止令牌,因此不能再次包含它。
旁注:
您不会在任何地方延长令牌的生命周期,因此即使用户继续在网站上进行交互,令牌过期时间也不会更新。您将需要手动刷新令牌/生成新令牌以实现滑动到期。
我建议您将令牌保存在 cookie 中。将 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));
【讨论】:
好的。看来我还有一些东西要在这里学习。所以我知道如何使用本地存储。我仍然需要弄清楚如何使令牌过期,这是我似乎无法弄清楚的。我想从我的 React 前端,我会从响应中获取令牌,然后将其保存到本地存储中。然后我必须弄清楚如何更新本地存储中的到期日期。你能告诉我如何在注销路由中使令牌过期吗? 如果它解决了您的问题并回答了您的问题,请记得accept the answer。 谢谢塞缪尔。我会试一试。感谢您的耐心等待。以上是关于使用 Express 和 MongoDB - 如何注销用户?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 nodejs / express 将数据存储在 mongodb 中?
如何填充递归模式引用 Mongodb、Mongoose、Express?
如何使用 mongodb、node.js、express 和 EJS 创建动态站点地图?
如何使用特定集合 Mongoose-Express-Mongodb (MEAN STACK)
如何使用 MongoDB + NodeJS Express 向 ReactJS React Router 和 Redux 添加登录身份验证和会话?