如何使用中间件为REST API创建用户组安全性

时间:2019-04-11 08:54:58

标签: javascript node.js rest

为了保护REST API,我正在使用中间件来检查用户的JWT令牌,并仅允许该特定用户访问自己的数据。

在auth.js中

const jwt = require('jsonwebtoken')
const User = require('../models/user')

const auth = async (req, res, next) => {
    try {
        const token = req.header('Authorization').replace('Bearer ', '')
        const decoded = jwt.verify(token, process.env.JWT_SECRET)
        const user = await User.findOne({ _id: decoded._id, 'tokens.token': token })

        if (!user) { // If no user is found
            throw new Error()
        }

        // if there's a user
        req.token = token
        req.user = user
        next()
    } catch (e) {
        res.status(401).send({ error: 'Please authenticate.' })
    }
}

module.exports = auth

在其中一个获取/更新路由器中

router.get('/itemShipmentStatus', auth, async (req, res) => {
   // Get the items shipment status from db.
})

但是,我注意到我需要创建一个新的admin用户(例如admin 1,admin2)来获取和更新所有用户的itemShipmentStatus。有没有一种方法可以通过中间件(auth.js?)实现用户组身份验证

更新

我能想到的唯一解决方案是在创建新用户时向用户文档添加另一个“ userGroup”字段。然后在中间件auth.js中添加另一个条件,以检查用户是否属于管理组。

if (!user || user.userGroup !== 'Admin') { // If no user is found
    throw new Error()
}

这是常规的做法吗?

2 个答案:

答案 0 :(得分:2)

我建议添加存储在用户中的permissions数组。这样,您将更加灵活。然后

const auth = (allowedPermission) => (async (req, res, next) => {
    try {
        const token = req.header('Authorization').replace('Bearer ', '')
        const decoded = jwt.verify(token, process.env.JWT_SECRET)
        const user = await User.findOne({ _id: decoded._id, 'tokens.token': token })

        if (!user) { // If no user is found
            throw new Error()
        }
        if (!user.permissions.includes(allowedPermission)){
            throw new Error() // Forbidden 403
        }


        // if there's a user
        req.token = token
        req.user = user
        next()
    } catch (e) {
        res.status(401).send({ error: 'Please authenticate.' })
    }
})

和在路线

router.get('/itemShipmentStatus', auth([admin, user]), async (req, res) => {
   // Get the items shipment status from db.
})

然后要确定要运行的正确代码。

我建议考虑api的划分。公用api和管理api。这是因为从概念上讲,用户可能希望成为管理员并访问其自己的itemShipmentStatus。因此拥有

router.get('/itemShipmentStatus', auth([admin, user]), async (req, res) => {
   // Get the items shipment status from db.
})
router.get('/admin/itemShipmentStatus', auth([admin]), async (req, res) => {
   // Get the items shipment status from db of all user.
})

这允许管理员用户以普通用户身份测试API并以管理员身份获得所有状态。

答案 1 :(得分:0)

更传统的方法是创建一个AuthRouter,它扩展了默认的express.Router并检查允许的角色,因此无需为每个路由使用中间件。

扩展express.Router来检查角色:

const express = require('express');
const jwt = require('jsonwebtoken');
const User = require('../models/user');

export default class AuthRouter extends express.Router {
  addRoles(...roles) {
    return this.use(checkAccessForRoles(...roles));
  }
}

const checkAccessForRoles = (...roles) => async (req, res, next) => {
  const token = req.header('Authorization').replace('Bearer ', '');
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  const user = await User.findOne({ _id: decoded._id, 'tokens.token': token });

  if (!roles.includes(user.role)) throw new Error('Forbidden');

  req.user = user;

  return next();
};

AuthRouter用户角色使用ADMIN

const adminRouter = new AuthRouter({
  prefix: '/admin',
})
  .addRoles(['ADMIN']);

adminRouter.get('/itemShipmentStatus', async (req, res) => {
  // Get the items shipment status from db.
});