如何在创建新用户后使我的令牌持久化?

huangapple go评论58阅读模式
英文:

How can i make my token to persist after creating a new user?

问题

我正在使用Express.js和MongoDB开发一个API。

用户可以登录。只有管理员用户才能访问/register来注册新用户。

因此,我希望只有管理员用户才能从应用程序内部创建新员工及其对应的用户。

在我的控制器auth.js中,我有以下方法:

exports.register = asyncHandler(async (req, res, next) => {
    const { email, password, name, lastName } = req.body;
    const user = await User.create({
        email,
        password
    });

    const employee = await Employee.create({
        name,
        lastName,
        user: user._id
    })

    res.status(200).json({
        success: true,
        user: user,
        employee: employee
    })

});
exports.login = asyncHandler(async (req, res, next) => {
    const { email, password } = req.body;

    // 验证邮箱和密码
    if (!email || !password) {
        return next(new ErrorResponse('请提供电子邮件和密码', 400));
    }

    // 检查用户
    const user = await User.findOne({ email }).select('+password');

    if (!user) {
        return next(new ErrorResponse('无效的凭据', 401));
    }

    // 检查密码是否匹配
    const isMatch = await user.matchPassword(password);

    if (!isMatch) {
        return next(new ErrorResponse('无效的凭据', 401));
    }

    sendTokenResponse(user, 200, res);

});
const sendTokenResponse = (user, statusCode, res) => {
    // 创建令牌
    const token = user.getSignedJwtToken();

    const options = {
        expires: new Date(
            Date.now() + process.env.JWT_COOKIE_EXPIRE * 24 * 60 * 60 * 1000
        ),
        httpOnly: true
    };

    if (process.env.NODE_ENV === 'production') {
        options.secure = true;
    }

    res
        .status(statusCode)
        .cookie('token', token, options)
        .json({
            success: true,
            token
        });
};
exports.getMe = asyncHandler(async (req, res, next) => {
    const user = await User.findById(req.user._id);

    res.status(200).json({
        success: true,
        data: user
    });

})

我还有以下中间件函数:

const jwt = require('jsonwebtoken');
const asyncHandler = require('./async');
const ErrorResponse = require('../utils/errorResponse');
const User = require('../models/User');

exports.protect = asyncHandler(async (req, res, next) => {
    let token;

    if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
        token = req.headers.authorization.split(' ')[1];
    } 

    if (!token) {
        return next(new ErrorResponse('未经授权访问此路由', 401));
    }

    try {
        const decoded = jwt.verify(token, process.env.JWT_SECRET);

        if (!decoded.id) {
            return next(new ErrorResponse('无效的令牌', 401));
        }

        req.user = await User.findById(decoded.id);
        if (!req.user) {
            return next(new ErrorResponse('找不到用户', 404));
        }

        next();
    } catch (error) {
        return next(new ErrorResponse('无效的令牌', 401));
    }
});

exports.authorize = (...roles) => {
    return (req, res, next) => {
        if (!roles.includes(req.user.role)) {
            return next(new ErrorResponse(`用户角色 ${req.user.role} 未被授权访问此路由`, 403));
        }
        next();
    }
}

登录功能正常运作,并且我可以获取当前登录的用户。如果登录的用户不是管理员,我会收到相应的错误消息。如果用户是管理员,我可以成功创建新用户。问题在于,创建新用户后,已登录用户的令牌被删除,没有持续存在。如果我再次访问api/auth/me端点,我会收到"无效的令牌"错误消息。

如何使我的令牌持续存在?因为也许我想用管理员用户创建多个用户而不被注销。

(Note: The above translation includes only the code-related parts and not the additional explanations or questions.)

英文:

I am developing an api with expressjs and mongodb.

The user can login. Only if the logged in user is an admin, then the user can access /register to register new users.

So i want that the only person able to create new employees and their respective user is the admin user, from inside the app.

I have the following methods in my controller auth.js:

exports.register = asyncHandler(async (req, res, next) => {
        const { email, password, name, lastName } = req.body;
        const user = await User.create({
            email,
            password
        });

        const employee = await Employee.create({
            name,
            lastName,
            user: user._id
        })

        res.status(200).json({
          success: true,
          user: user,
          employee: employee
        })
    
});

// @desc   Loginuser
// @route  POST /api/v1/auth/login
// @access Public

exports.login = asyncHandler(async (req, res, next) => {
    const { email, password } = req.body;
  
    // Validate emil & password
    if (!email || !password) {
      return next(new ErrorResponse('Please provide an email and password', 400));
    }
  
    // Check for user
    const user = await User.findOne({ email }).select('+password');
  
    if (!user) {
      return next(new ErrorResponse('Invalid credentials', 401));
    }
  
    // Check if password matches
    const isMatch = await user.matchPassword(password);
  
    if (!isMatch) {
      return next(new ErrorResponse('Invalid credentials', 401));
    }

    sendTokenResponse(user, 200, res);

  });

// Get token from model, create cookie and send response
const sendTokenResponse = (user, statusCode, res) => {
    // Create token
    const token = user.getSignedJwtToken();
  
    const options = {
      expires: new Date(
        Date.now() + process.env.JWT_COOKIE_EXPIRE * 24 * 60 * 60 * 1000
      ),
      httpOnly: true
    };
  
    if (process.env.NODE_ENV === 'production') {
      options.secure = true;
    }
  
    res
      .status(statusCode)
      .cookie('token', token, options)
      .json({
        success: true,
        token
      });
  };

  // @desc   Get current logged in user
  // @route  POST /api/auth/me
  // @access Private

  exports.getMe = asyncHandler(async (req, res, next) => {
    const user = await User.findById(req.user._id);
  
    res.status(200).json({
      success: true,
      data: user
    });
  
  })

And i have the following middleware functions:

const jwt = require('jsonwebtoken');
const asyncHandler = require('./async');
const ErrorResponse = require('../utils/errorResponse');
const User = require('../models/User');

// Protect Routes 
exports.protect = asyncHandler(async (req, res, next) => {
    let token;
  
    if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
      token = req.headers.authorization.split(' ')[1];
    } /* else if (req.cookies.token) {
        token = req.cookies.token;
    } */
  
    // Make sure token exists
    if (!token) {
      return next(new ErrorResponse('Not authorized to access this route', 401));
    }
  
    try {
      // Verify token
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
  
      // Check if decoded has id property
      if (!decoded.id) {
        return next(new ErrorResponse('Invalid token', 401));
      }
  
      req.user = await User.findById(decoded.id);
      if (!req.user) {
        return next(new ErrorResponse('User not found', 404));
      }
  
      next();
    } catch (error) {
      return next(new ErrorResponse('Invalid token', 401));
    }
  });
  


//Grant access to specific roles
exports.authorize = (...roles) => {
    return (req, res, next) => {
        if(!roles.includes(req.user.role)) {
            return next(new ErrorResponse(`User role ${req.user.role} is not authorized to access this route`, 403));
        }
        next();
    }
}

The log in works fine and i can get the current logged in user. And if the logged in user is not an admin i get the corresponding error message. If the user is an admin i am being able to create a new user. The issue is that after creating a new user, the token of the already logged in user is deleted, it is not persisting. And if i go again to the endpoint api/auth/me i get the error message of Invalid token.

How can i make my token to persist? Because maybe with the admin user i want to create more than one user without being logged out.

答案1

得分: 0

当然,令牌被删除,因为您没有在注册函数的响应中重新发送令牌。

如果您在注册部分添加sendTokenResponse(req.user, 200, res);,您将创建一个新的令牌。

您可以在下面的代码中使用现有令牌。对于您的情况,您的代码应该如下所示:

exports.register = asyncHandler(async (req, res, next) => {
    /* 
     * 检查会话
     * 如果有会话并且已登录的用户是管理员
     * 或者
     * 如果您已经获得了令牌访问权限,请跳过此部分并在响应中重新发送令牌
     */
    const { token } = req.cookies;

    // 在这里应该有两种不同的响应
    // 这是用于已登录用户作为管理员的情况
    res
        .status(200)
        .cookie('token', token, options)
        .json({
            success: true,
            user: user,
            employee: employee
        })

    // 这是用于非会话情况的情况
    res.status(200).json({
        success: true,
        user: user,
        employee: employee
    })

});

我告诉过您如何做,希望您能编写出它:)。

注意:我认为,与其让函数变得如此复杂,您应该编写一个名为addUser的新函数,并仅授予管理员访问权限,当然您应该在响应中重新发送令牌。

英文:

Of course the token is deleted because you don't resend the token in the response of register function.

If you add sendTokenResponse(req.user, 200, res); to register section, you'll create a new token.

You can use existing token in below code. For your case your code should be like,

exports.register = asyncHandler(async (req, res, next) => {
        /* 
         * Check the session
         * If there is a session and logged in user is an admin
         * OR
         * If you already have access the token, skip this  
         * section and resend the token in response
         */
        const { token } = req.cookies;
        
        .
        .
        .

        // There should be two different responses here too
        // This is for logged in user as an admin
        res
         .status(200)
         .cookie('token', token, options)
         .json({
          success: true,
          user: user,
          employee: employee
        })

        // This is for non-session cases
        res.status(200).json({
          success: true,
          user: user,
          employee: employee
        })
    
});

I told you how to do it, I hope you can code it :).

NOTE: I think instead of complicating the function that much, you should write a new function called addUser and give access to only admin and ofc you should resend the token in response.

huangapple
  • 本文由 发表于 2023年6月6日 03:32:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76409497.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定