Mongodb - пользователи, роли и группы с JWT + REST API - PullRequest
1 голос
/ 01 июня 2019

Intro

Я реализую простое MEAN-приложение с токеном JWT для аутентификации.Это приложение позволяет вам создать учетную запись, а затем создать собственную группу или присоединиться к существующей.У каждой группы есть определенные роли, которые администратор группы назначает каждому пользователю.Вот мои схемы, которые, надеюсь, помогут вам лучше понять мое решение.

Схема пользователя

_id: {
  type: String,
  default: function () { return new mongo.ObjectId().toString() }
},
email: {
  type: String,
  required: true,
  unique: true
},
displayName: {
  type: String
},
hash: {
  required: true,
  type: String
}

Схема группы

_id: {
  type: String,
  default: function () { return new mongo.ObjectId().toString() }
},
groupName: String,
ownerUser: {
  type: String,
  ref: 'User'
},
userList: [
  {
    user: {
      type: String,
      ref: 'User'
    },
    role: {
      type: String,
      ref: 'Role'
    }
  }
]

Схема роли

_id: {
  type: String,
  default: function () { return new mongo.ObjectId().toString() }
},
groupId: String,
roleName: String,
access: [{
  path: String,
  allowed: Boolean
}]

Как это работает

Регистрация пользователя / вход в систему -> Создать группу или присоединиться к существующей -> Администратор группы назначает роль пользователю.

  • В группах будет примерно 250 пользователей.
  • В группе может быть несколько ролей (10-15).
  • Пользователь может присоединиться к нескольким группам

Экспресс-часть

пример маршрута

router.get('/restricted', JWTAuth, userProject, userRole, (res, req, next) => {
 res.send("ok") 
})

access.js

export const JWTAuth = passportAuth('jwt', {session: false});

export const userProject = async (req, res, next) => {
  const userProject = await GroupModel.findOne({ _id: req.body.groupId, "userList.user" : req.user._id })
  if(userProject) {
    req.project = userProject;
    next()
  }else{
    res.sendStatus(401);
  }
}

export const userRole = async (req, res, next) => {
  const roleId = arrayFindByObjectValue(req.user._id, 'user', req.project.userList).role;
  const userRole = await RoleModel.findOne({ _id: roleId })
  req.role = userRole;
  next();
}

export const arrayFindByObjectValue = (value, key, array) => {
  for (var i = 0; i < array.length; i++) {
      if (array[i][key] === value) {
          return array[i];
      }
  }
}

другие попытки .. заполнить (любые улучшения в скорости)

export const userRolePopulate = async (req, res, next) => {
  const userProject = await GroupModel.findOne({ _id: req.body.groupId, "userList.user" : req.user._id }, {"userList.$" : 1}).populate('userList.role');
  req.role = userProject.userList[0].role;
  next()
}

и агрегация (улучшение 15%)

const userProject: any = await GroupModel.aggregate([
  {$match: {_id: req.body.groupId}},
  {$project: {
    userList: {$filter: {
        input: '$userList',
        as: 'user',
        cond: {$eq: ['$$user.user', req.user._id]}
    }},
    _id: 0
  }},
])

Вопрос

Теперь я надеюсь, вы понимаете мою ситуацию.При таком подходе мне нужно выполнить несколько запросов к базе данных, чтобы получить роль пользователя только для простого запроса.Я беспокоюсь о скорости.Каждый запрос занимал около 150 мсек с промежуточным программным обеспечением роли (1000 пользователей группы).Я думаю, что это решение будет излишне перегружать сервер.Есть ли лучшее решение?Должен ли я использовать какой-либо сеанс (без сохранения состояния JWT ..) или другой метод для временного хранения роли пользователя?Есть ли лучший дизайн базы данных для этого метода?

Я прочитал много статей о системах на основе ролей, но я никогда не сталкивался с этим конкретным решением.Спасибо за ответ.Если вам нужна дополнительная информация, пожалуйста, дайте мне знать, и я отредактирую свой ответ.

...