如何处理每个后续用户的快速会话?

问题描述 投票:0回答:1

我目前正在研究用户身份验证并将 JWT 令牌存储在 MongoDB 会话集合中。但是,我在管理多个会话时遇到了问题。具体来说,在第一个用户成功登录后,我无法为每个后续用户生成和存储会话。

我在 VS Code 和 MongoDB Compass 中使用 Thunder Client。当我使用以下凭据成功登录时:

{
  "email": "[email protected]",
  "password": "asdfAsdf1!"
}

成功创建会话并将其存储在会话集合中,并生成 connect.sid cookie。但是,当我尝试使用一组不同的凭据登录时:

{
  "email": "[email protected]",
  "password": "asdfAsdf2!"
}

并且与 [电子邮件受保护] 的凭据关联的会话 ID 正在重新生成为新 ID,而不是创建全新的会话且不更改之前生成的会话。

请提供如何解决此问题的建议?

这是我的代码

app.use(
  session({
    secret: "key_that_will_sign_the_cookie",
    resave: false,
    saveUninitialized: false,
    store: MongoStore.create({
      mongoUrl: "mongodb://127.0.0.1:27017/addressBook",
      collectionName: "sessions",
    }),
    cookie: {
      secure: false,
      maxAge: 15 * 60 * 1000,
    },
  })
); 

userController.ts

export const logIn = asyncHandler(async (req: Request, res: Response) => {
  const { email, password } = req.body;
  const user = await userService.login(email, password);
  const token = tokenService.generateToken(
    user._id,
    "ACCESS_TOKEN_PRIVATE_KEY_HERE"
  );

  // Set session
  (req.session as MySession).jwtToken = token;

  res.status(200).json({
    success: true,
    message: "You have successfully logged in.",
  });
});


export const logout = asyncHandler(async (req: Request, res: Response) => {
  req.session.destroy((err) => {
    if (err) {
      console.log("Error destroying session", err.message);
      res.status(500).json({
        success: false,
        message: "Error logging out.",
      });
    } else {
      res.clearCookie("connect.sid");
      res
        .status(200)
        .json({ success: true, message: "Successfully logged out." });
    }
  });
});

requireAuth.ts(中间件)

const requireAuth = async (req: Request, res: Response, next: NextFunction) => {
  try {
    const { jwtToken } = (req.session as MySession) || {};

    if (!jwtToken) {
      return res.status(401).json({ messagE: messages.unauthorized });
    }

    const { sub: userId } = jwt.verify(
      jwtToken,
      "ACCESS_TOKEN_PRIVATE_KEY_HERE"
    );

    const user = await User.findOne({ _id: userId });

    if (!user) {
      return res.status(401).json({ messagE: messages.unauthorized });
    }

    req.user = user;
    next();
  } catch (error: any) {
    return res.status(401).json({ message: error.message });
  }
};

更新 @jfriend00

使用会话背后的想法是组织代码并将逻辑拆分为多个文件。业务逻辑服务,但在我现在的情况下可能是多余的(稍后我将重构代码)。

jwt
是 jsonwebtoken,我使用它来生成身份验证令牌,并将其存储在会话中 - 参见屏幕截图。稍后在
requireAuth
中间件中,我验证该 jwt 令牌的有效性并用于保护所有联系路由。

    router.use(requireAuth);

    router
      .get("/", getAllContacts)
      .get("/favorite", getFavoriteContacts)
      .get("/:contactId", getContactById)
      .post("/", addNewContact)
      .patch("/:contactId", updateContact)
      .delete("/:contactId", deleteContact);

tokenService.ts

import jwt from "jsonwebtoken";
import mongoose from "mongoose";

export const generateToken = (
  userId: mongoose.Types.ObjectId,
  secret: string
): string => {
  const payload = {
    sub: userId,
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + 15 * 60,
  };

  return jwt.sign(payload, secret);
};

export default { generateToken };

userService.ts

const login = async (email: string, password: string): Promise<IUserDoc> => {
  loginValidation(email, password);

  const user = await getUserByEmail(email);
  const isValidUser = user && (await user.isPasswordMatch(password));

  if (!isValidUser) {
    throw new ApiError(400, messages.incorrectCredentials);
  }

  return user;
};

屏幕截图中捕获的会话是我使用一名用户登录时存储的会话(例如电子邮件:[email protected])。随后,当我输入第二个用户的凭据并单击“登录”时,为前一个用户存储的会话的到期日期将被更新,并且还会生成新的 jwtToken。为什么会发生这种情况?是否应该独立于第一个会话创建新会话?另外我想提一下 cookie connect.sid 和以前一样,没有改变。

是的,我没有两个单独的会话对象,而是只有一个,并且在每个后续用户登录后,仅增加到期日期并更新 jwtToken。 (我还注意到数据库的时区完全错误,我需要检查一下。)

对于之前的任何困惑,我深表歉意。

express express-session
1个回答
0
投票

如果您有明确的“注销”功能,您可以清除或删除先前的会话,但您不能指望这一点,因为用户在以其他人身份登录之前不需要这样做。也许您应该做的是,当您登录用户时,您应该首先将会话对象重新初始化为空(清除所有先前的数据),这样就不存在从一个用户到下一个用户的污染风险。请注意,从不同浏览器登录之间这都不是问题,因为它们永远不会共享相同的会话对象。

是否应该独立于第一个会话创建新会话?另外我想提一下 cookie connect.sid 与以前保持相同,没有改变。

来自express-session的会话对象与登录无关。您的登录是会话之上的一个完全不同的层。会话对象连接到
connect.sid

cookie。如果客户端提供相同的

connect.sid
cookie,那么您的服务器将获取相同的会话对象。因此,正如我之前所解释的,如果您在浏览器 1 中以 userA 身份登录,则会将 userA 的登录数据填充到与浏览器 1 中的 cookie 关联的会话对象中。如果您随后在会话 cookie 之前在浏览器 1 中以 userB 身份登录过期后,它仍然具有相同的
connect.sid
cookie,因此它仍然会在服务器上使用完全相同的会话对象。然后,您的代码会将 userB 的数据放入同一个会话对象中。
请记住,express-session 是在特定浏览器中处理 cookie,与您自己的登录过程完全无关。您的登录过程只是使用底层会话对象来存储登录数据。

© www.soinside.com 2019 - 2024. All rights reserved.