Node.js 快速路由和jsonwebtoken,在创建令牌后保持登录状态

Node.js 快速路由和jsonwebtoken,在创建令牌后保持登录状态,node.js,express,jwt,http-headers,request-headers,Node.js,Express,Jwt,Http Headers,Request Headers,在Express中构建基于角色的访问控制api时,我很难连接上一个点。 并在我现有的程序上实现,但我认为我错过了最后一步,在无数的教程分析瘫痪之后。从那以后,我将所有必要的代码缩减到我认为是最小的 目前我可以创建一个新用户并将其保存到mongoose数据库。我可以看到bcrypt的hash正在做它的事情,我可以看到在注册后的响应中生成的令牌。然而,在注册或登录后,一旦我导航到一个新页面,例如用户自己的id页面/user/:userId,根据教程,我会不断得到您需要登录。我知道我需要在每个请求上检

在Express中构建基于角色的访问控制api时,我很难连接上一个点。 并在我现有的程序上实现,但我认为我错过了最后一步,在无数的教程分析瘫痪之后。从那以后,我将所有必要的代码缩减到我认为是最小的

目前我可以创建一个新用户并将其保存到mongoose数据库。我可以看到bcrypt的hash正在做它的事情,我可以看到在注册后的响应中生成的令牌。然而,在注册或登录后,一旦我导航到一个新页面,例如用户自己的id页面
/user/:userId
,根据教程,我会不断得到
您需要登录
。我知道我需要在每个请求上检查令牌,但我的问题是,为什么中间件似乎没有检查令牌,或者有什么东西阻止了它

由于令牌显示在json响应中,我当然应该能够在下一个get请求中检查令牌是否存在,例如
/user/:userId
页面?不是这样吗?或者浏览器只是显示响应,但我仍然需要实际存储响应?可以说,我不明白它去哪儿了

有什么建议吗?或者这是一个会议的事情?我知道没有所有的代码有点困难,但如果有人能发现任何相关的东西,以便我可以研究我的下一步,我将不胜感激

首先,在app.js中使用该中间件

app.use(express.json());
app.use(express.urlencoded({extended: true}));

app.use('/', async (req, res, next) => {
  if (req.headers['x-access-token']) {
    try {
      const accessToken = req.headers['x-access-token'];
      const {userId, exp} = await jwt.verify(accessToken, process.env.JWT_SECRET);
      console.log('token verified'); // not printing to console
      // If token has expired
      if (exp < Date.now().valueOf() / 1000) {
        return res.status(401).json({
          error: 'JWT token has expired, please login to obtain a new one',
        });
      }
      res.locals.loggedInUser = await User.findById(userId);
      next();
    } catch (error) {
      next(error);
    }
  } else {
    next();
  }
});
根据教程布线示例

router.get('/signup', (req, res, next) => {
  res.render('signup', {
    viewTitle: 'User SignUp',
  });
});

router.post('/signup', userController.signup);

router.get('/login', (req, res, next) => {
  res.render('login', {
    viewTitle: 'User Login - WTCT OPS',
  });
});

router.post('/login', userController.login );

router.get('/add', userController.allowIfLoggedin, userController.grantAccess('readAny', 'profile'), userController.add);

router.get('/users', userController.allowIfLoggedin, userController.grantAccess('readAny', 'profile'), userController.getUsers);

router.get('/user/:userId', userController.allowIfLoggedin, userController.getUser);

router.put('/user/:userId', userController.allowIfLoggedin, userController.grantAccess('updateAny', 'profile'), userController.updateUser);

router.delete('/user/:userId', userController.allowIfLoggedin, userController.grantAccess('deleteAny', 'profile'), userController.deleteUser);
控制器的相关部分

async function hashPassword(password) {
  return await bcrypt.hash(password, 10);
}

async function validatePassword(plainPassword, hashedPassword) {
  return await bcrypt.compare(plainPassword, hashedPassword);
}


// grant access depending on useraccess role
exports.grantAccess = function(action, resource) {
  return async (req, res, next) => {
    try {
      const permission = roles.can(req.user.role)[action](resource);
      if (!permission.granted) {
        return res.status(401).json({
          error: 'You don\'t have enough permission to perform this action',
        });
      }
      next();
    } catch (error) {
      next(error);
    }
  };
};

// allow actions if logged in
exports.allowIfLoggedin = async (req, res, next) => {
  try {
    const user = res.locals.loggedInUser;
    if (!user) {
      return res.status(401).json({
        error: 'You need to be logged in to access this route',
      });
    }
    req.user = user;
    next();
  } catch (error) {
    next(error);
  }
};

// sign up
exports.signup = async (req, res, next) => {
  try {
    const {role, email, password} = req.body;
    const hashedPassword = await hashPassword(password);
    const newUser = new User({email, password: hashedPassword, role: role || 'basic'});
    const accessToken = jwt.sign({userId: newUser._id}, process.env.JWT_SECRET, {
      expiresIn: '1d',
    });
    newUser.accessToken = accessToken;
    await newUser.save();
    res.send({
      data: newUser,
      message: 'You have signed up successfully',
    });
  } catch (error) {
    next(error);
  }
};

exports.login = async (req, res, next) => {
  try {
    const {email, password} = req.body;
    const user = await User.findOne({email});
    if (!user) return next(new Error('Email does not exist'));
    const validPassword = await validatePassword(password, user.password);
    if (!validPassword) return next(new Error('Password is not correct'));
    const accessToken = jwt.sign({userId: user._id}, process.env.JWT_SECRET, {
      expiresIn: '1d',
    });
    await User.findByIdAndUpdate(user._id, {accessToken});
    res.status(200).json({
      data: {email: user.email, role: user.role},
      accessToken,
    });
  } catch (error) {
    next(error);
  }
};


// get one user
exports.getUser = async (req, res, next) => {
  try {
    const userId = req.params.userId;
    const user = await User.findById(userId);
    if (!user) return next(new Error('User does not exist'));
    // console.log(req.params);
    res.send(200).json({
      data: user,
    });
  } catch (error) {
    next(error);
  }
};
为什么当试图发布到端点
/user/:userId
时,中间件没有检查令牌

谢谢你的建议

更新:

到目前为止,我已尝试从app.use中删除
/
。我看到我现在犯了这个错误,但也尝试将其从
app.use(userRoutes)中删除中间件,使其应用于所有http请求,但运气不佳

  app.use(async (req, res, next) => {
  if (req.headers['x-access-token']) {
    try {
      const accessToken = req.headers['x-access-token'];
      const {userId, exp} = await jwt.verify(accessToken, process.env.JWT_SECRET);
      // If token has expired
      if (exp < Date.now().valueOf() / 1000) {
        return res.status(401).json({
          error: 'JWT token has expired, please login to obtain a new one',
        });
      }
      res.locals.loggedInUser = await User.findById(userId);
      // console.log('Time:', Date.now());
      next();
    } catch (error) {
      next(error);
    }
  } else {
    next();
  }
});

app.use(userRoutes);
app.use(异步(请求、恢复、下一步)=>{
if(请求头['x-access-token']){
试一试{
const accessToken=req.headers['x-access-token'];
const{userId,exp}=wait jwt.verify(accessToken,process.env.jwt_SECRET);
//如果令牌已过期
如果(exp
我还认为,可能是因为我的服务器在后端发出http请求,这可能导致设置
x-access-token
头时出现问题?因此,我尝试将
x-access-token
mw改为使用
路由器。在所有路由上使用
,但仍然没有。我不明白我错过了什么。为了确保我没有遗漏一些基本的东西,因为我使用JWT,我不需要使用本地存储或cookies来允许在登录时在页面之间浏览,因为我可以使用标题中设置的令牌,对吗


再次感谢您的建议

这是因为您的中间件只绑定到
/
路由。如果要在每条路线上使用它,请将其删除。看看ExpressJS中关于中间件的内容。

您是指app.js中检查令牌的中间件吗?或者,如果我删除
app.use('/',userRoutes)那么如何定义我的路线?谢谢你的建议!我想它一定是与中间件有关,所以我会进一步检查它!您现在正在做的是将您的auth中间件绑定到
/
路由(第一个cod示例),删除它,然后您的中间件将可用于整个应用程序。您的意思是在x-access-token中间件中,我应该删除/以便它可以在所有请求上运行吗?还是整个街区?我认为app.use/然后匹配了所有路由,我确实声明了app.use(“/”,userRoutes);就在第一个示例代码下面。再次感谢,对不起。我一定会特别仔细地看这些文件。都很好!不要道歉,每个人都会这样:)。如果我的答案解决了您的问题,请将其标记为正确,以便其他有相同问题的人可以使用您的问题。再次感谢您,晚餐后我将尝试一下,发布我的结果,并确实将您的答案标记为正确。我想补充一下我所做的修改,以供其他人参考。
  app.use(async (req, res, next) => {
  if (req.headers['x-access-token']) {
    try {
      const accessToken = req.headers['x-access-token'];
      const {userId, exp} = await jwt.verify(accessToken, process.env.JWT_SECRET);
      // If token has expired
      if (exp < Date.now().valueOf() / 1000) {
        return res.status(401).json({
          error: 'JWT token has expired, please login to obtain a new one',
        });
      }
      res.locals.loggedInUser = await User.findById(userId);
      // console.log('Time:', Date.now());
      next();
    } catch (error) {
      next(error);
    }
  } else {
    next();
  }
});

app.use(userRoutes);