快速路由和jsonwebtoken,在创建令牌后保持登录状态 [英] Express routing and jsonwebtoken, staying logged in after token creation

查看:66
本文介绍了快速路由和jsonwebtoken,在创建令牌后保持登录状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很难连接最后一个点,从而在Express中构建基于角色的访问控制api.遵循本教程并实施迁移到我现有的程序上,但是我想我错过了最后一步,并且在无数的教程分析陷入瘫痪之后.我将所有必要的代码缩减到我认为是最低限度的水平.

I'm having a hard time connecting the last dots building a role based access control api in Express. Following this tutorial and implementing onto my existing program, but I think I am missing the last step and after countless tutorials analysis paralysis has set in. I have since scaled back all my necessary code to what I think is the bare minimum.

当前,我能够创建一个新用户并将其保存到mongoose数据库中.我可以看到bcrypt的哈希正在完成它的工作,并且可以看到注册后响应中正在生成令牌.但是,一旦我在注册或登录后导航到新页面(例如,根据教程,用户拥有自己的id页面/user/:userId ),我就会不断收到您需要登录.我知道我需要在每个请求中检查一个令牌,但是我的问题是,为什么中间件似乎不检查令牌还是有东西阻止它?

Currently I am able to create a new user and save them to the mongoose database. I can see the hash by bcrypt is doing its thing and I can see the token being generated in the response after signing up. However as soon as I navigate to a new page after signup or login, for eg the users own id page/user/:userId as per tutorial, I keep getting You need to be logged in. I know I need to check for a token on every request but my question is, why doesn't it seem like the middleware is checking for the token or something is holding it back?

由于令牌肯定显示在json响应中,因此我应该能够通过例如/user/:userId 页面的下一个get请求来检查令牌是否存在?那不是主意吗?还是浏览器只是显示响应,但我仍然需要实际存储它?我不知道该怎么说..

Since the token is shown in the json reponse surely I should be able to check for the tokens existence with the next get request at for eg the /user/:userId page? Isn't that the idea? Or is the browser just showing the response but I still need to actually store it? I don't understand where it goes to so to speak..

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

Any advice? Or is this a session thing? I know its a bit hard without all the code but if anyone could spot anything relevant so that I could research my next steps I would much appreciate it!

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

First this middleware in 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();
  }
});

app.use('/',userRoutes);

app.use('/', userRoutes);

我已经使用必需的模块 access-control 构建了角色

I have built the roles using the module access-control which is required

const AccessControl = require('accesscontrol');
const ac = new AccessControl();

exports.roles = (function() {
  ac.grant('basic')
      .readOwn('profile')
      .updateOwn('profile');

  ac.grant('supervisor')
      .extend('basic')
      .readAny('profile');

  ac.grant('admin')
      .extend('basic')
      .extend('supervisor')
      .updateAny('profile')
      .deleteAny('profile');

  return ac;
})();

按照教程路由示例.

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 时,中间件不检查令牌?

Why when trying to post to the endpoint /user/:userId is the middleware not checking for the token?

谢谢您的任何建议!

更新:

到目前为止,我已经尝试从app.use中删除/.我看到我现在犯了这个错误,但是还尝试从 app.use(userRoutes); 中间件中删除它,使它适用于所有http请求,但没有运气.

So far I have tried to removed the / from app.use. I saw I made that mistake now, but also tried removing it from the app.use(userRoutes); middleware to make it apply to all http requests but no luck.

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

我还认为这可能是因为我的服务器在后端发出了http请求,也许是在设置 x-access-token 标头时出现了问题?因此,我尝试将 x-access-token mw更改为在所有路由上都使用 router.use ,但仍然一无所获.我不明白我在想什么.并且只是为了确保我没有遗漏一些基本知识,因为我使用的是JWT,所以我无需使用本地存储或cookie即可在登录时在页面之间进行浏览,因为我可以使用标头中设置的令牌,?

I also thought that maybe because my server makes http requests in the backend maybe that was causing a problem in setting the x-access-token header? So I tried to change the x-access-token mw to use router.use on all routes but still nothing. I don't understand what I am missing. And just to be sure I'm not missing something fundamental, since I am using the JWT I do not need to use local storage or cookies to allow for browsing between pages while logged in since I can use the token set in the header, correct?

再次感谢您的任何建议!

Thanks again for any advice!

推荐答案

这是因为您的中间件仅绑定到/路由.如果您希望将其用于所有路线,请将其删除.看看ExpressJS 关于中间件的文档.

That's because your middleware is only tied to the / route. Remove it if you want it to be used for every route. Take a look at the ExpressJS Docs regarding middleware.

这篇关于快速路由和jsonwebtoken,在创建令牌后保持登录状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆