Экспресс промежуточного программного обеспечения: разрешить доступ только администратору или модератору

голоса
0

Я хочу , чтобы маршрут , чтобы быть доступным только модератор или администратор. Я пытался применить промежуточное программное обеспечение массива на маршруте. Но, он просто запрещает доступ , если один промежуточные не может применяться.
так, сказать , что я администратор или модератор , я могу получить доступ к /store-detail.
Но вот, если я админ не могу получить доступ к нему , потому что проверить замедлитель , а также.
Здесь _both промежуточное программное adminи moderatorприкладывается.
Я хочу, чтобы применить adminили moderator.
Как я могу сделать только один из них будет применяться?
Так что только администратор или модератор может получить к нему доступ.
verifyпромежуточное программное обеспечение является проверка JWT фишку.
маршрутизатор

router.post('/store-detail', [verify, admin, moderator], async (req, res) => {
    //validate data
}}

промежуточное программное

const User = require('../models').User

module.exports = async function (req, res, next) { 
    // 401 Unauthorized
    const user = await User.findOne({ where: { id : req.user.id}})
    if(user.role !== 'moderator') return res.status(403).send({error: { status:403, message:'Access denied.'}});
    next();
  }
const User = require('../models').User

module.exports = async function (req, res, next) { 
    // 401 Unauthorized
    const user = await User.findOne({ where: { id : req.user.id}})
    if(user.role !== 'admin') return res.status(403).send({error: { status:403, message:'Access denied.'}});
    next();
  }
Задан 09/10/2019 в 12:52
источник пользователем
На других языках...                            


4 ответов

голоса
1

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

module.exports = function hasRole(roles) {
  return async function(req, res, next) {
    const user = await User.findOne({ where: { id: req.user.id } });
    if (!user || !roles.includes(user.role) {
      return res.status(403).send({error: { status:403, message:'Access denied.'}});
    }
    next();
  }
}

И использовать промежуточное программное обеспечение, как это:

router.post('/store-detail', verify, hasRole(['admin', 'moderator']), async (req, res) => {})
Ответил 09/10/2019 в 13:09
источник пользователем

голоса
0

JWT решение на основе: (извините за длинный пост, если это неподходящее, я могу удалить)

пользователей маршрут (маршруты / users.js) Здесь я возвращаю аутентификации-маркер в операции входа в систему.

const auth = require("../middleware/auth");
const hasRole = require("../middleware/hasRole");
const bcrypt = require("bcryptjs");
const { User } = require("../models/user");
const express = require("express");
const router = express.Router();

router.post("/register", async (req, res) => {
  const { name, email, password } = req.body;

  let user = await User.findOne({ email });
  if (user) return res.status(400).send("User already registered.");

  user = new User({ name, email, password });
  const salt = await bcrypt.genSalt(10);
  user.password = await bcrypt.hash(user.password, salt);
  await user.save();

  const token = user.generateAuthToken();
  res.header("auth-token", token).send({ name, email });
});

router.post("/login", async (req, res) => {
  const { email, password } = req.body;

  let user = await User.findOne({ email });

  if (!user) return res.status(400).send("Invalid email or password.");

  const validPassword = await bcrypt.compare(password, user.password);

  if (!validPassword) return res.status(400).send("Invalid email or password.");

  const token = user.generateAuthToken();

  res.send(token);
});

module.exports = router;

user.js (User Model) Здесь я добавить информацию о роли в JWT при подписании его:

const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true,
    unique: true
  },
  password: {
    type: String,
    required: true
  },
  role: {
    type: String,
    default: "user"
  }
});

userSchema.methods.generateAuthToken = function() {
  const token = jwt.sign(
    { _id: this._id, role: this.role },
    process.env.JWT_PRIVATE_KEY
  );
  return token;
};

const User = mongoose.model("User", userSchema);

exports.User = User;

промежуточный слой / auth.js Здесь я аутентификация пользователя и добавление декодируется маркер запроса, так что мы можем использовать в следующем промежуточном программном.

const jwt = require("jsonwebtoken");

module.exports = function(req, res, next) {
  const token = req.header("auth-token");
  if (!token) return res.status(401).send("Access denied. No token provided.");

  try {
    const decoded = jwt.verify(token, process.env.JWT_PRIVATE_KEY);
    req.user = decoded;
    next();
  } catch (ex) {
    res.status(400).send("Invalid token.");
  }
};

промежуточный слой / hasRole.js Здесь мы проверяем, если user.role удовлетворяет необходимые роли.

module.exports = function hasRole(roles) {
  return function(req, res, next) {
    if (!req.user.role || !roles.includes(req.user.role)) {
      return res.status(403).send("Access denied.");
    }

    next();
  };
};


Наконец мы добавим следующий путь к маршруту пользователей, чтобы иметь возможность проверить разрешение на роль.

router.get("/me", [auth, hasRole(["admin", "moderator"])], async (req, res) => {
  const user = await User.findById(req.user._id).select("-password");
  res.send(user);
});

Тестировать:

Создание 3 пользователей отдельно с последующим с POST на следующий адрес

HTTP: // локальный: 3000 / API / пользователей / регистр

{
    "name": "admin",
    "email": "admin@so.com",
    "password" : "123456"
}

{
    "name": "moderator",
    "email": "moderator@so.com",
    "password" : "123456"
}

{
    "name": "user",
    "email": "user@so.com",
    "password" : "123456"
}

В MongoDB, в коллекции пользователей мы обновляем роль свойство администратора для пользователя с правами администратора, и замедлителя для пользователя замедлителя.

Используя почтальона или аналогичный инструмент, мы войти в систему с администратора или модератора полномочий.

В ответ мы получаем маркер аутентификации, мы используем его в заголовке Идент-маркер для этой защищенной конечной точки в запросе GET.

HTTP: // локальный: 3000 / API / пользователей / меня

Вы получите код статуса 200.

На этот раз мы войти в систему с обычными учетными данными. В ответ, мы копируем маркер аутентификации, а также использовать его в заголовке Идент-маркер для этой защищенной конечной точки в запросе GET.

HTTP: // локальный: 3000 / API / пользователей / меня

Вы получите код состояния 403 Forbidden.

Ответил 09/10/2019 в 14:04
источник пользователем

голоса
0

Ненужная часть

Из архитектуры системы точки зрения, вот что я буду делать.

  1. Вместо того, чтобы запрос сети внутри промежуточного слоя каждый раз, сохранить уровень доступа пользователя в маркер (скажем JWT маркер).

  2. Логин момент пользователь системы, сделать запрос к серверной, бэкенд будет сделать запрос к БД, чтобы retrive детали (которые следует учитывать уровень доступа) и шифруют данные пользователя в маркер. Ваш маркер должен содержать уровень доступа.

  3. На каждом запросе, Вы будете иметь маркер в заголовках, де-крипте маркера и установить детали уровней доступа (res.locals должен быть идеальным местом для установки маркеров).

   const verifyUser = async (req, res, next) => {
        const token = req.cookies.userId
        if(token) {
          try {
            const tokenVerficiation = await verifyToken(token)
            res.locals.userId = tokenVerficiation.userId
            next()
          } catch (error) {
            return res.status(401).send(`Invalid Access token`)
          }
        } else { 
         return res.status(401).send(`Not Authorized to view this.`)
        }
    }

Соответствующая часть

Так как вы настраиваете res.status(403).send({error: { status:403, message:'Access denied.'}});это имеет смысл , почему он не будет двигаться к следующему ППО.

Так как для АНИ звонка можно *Probablyотправить заголовки сразу , как предложено Жереми L создание одного нового промежуточного уровня будет наилучшим подходом

module.exports = function hasRole(roles) {
  return async function(req, res, next) {
    const user = await User.findOne({ where: { id: req.user.id } });
    if (!user || !roles.includes(user.role) {
      return res.status(403).send({error: { status:403, message:'Access denied.'}});
    }
    next();
  }
}
Ответил 09/10/2019 в 13:59
источник пользователем

голоса
0

Создать новую функцию , например , isAuthorizedдля обработки этой логики.

Если вы звоните по [verify, admin, moderator]отдельности они не зависят друг от друга.

маршрут:

router.post('/store-detail', isAuthorized, async (req, res) => {
    //validate data
}}

function isAuthorized(req, res, next) {
  if (!verify(req)) 
    return next();
  if (!admin(req) && !moderator(req)) 
    return res.status(403).send({error: { status:403, message:'Access denied.'}});
}

промежуточный слой:

const User = require('../models').User

module.exports = async function (req, res, next) { 
  // 401 Unauthorized
  const user = await User.findOne({ where: { id : req.user.id}})
  if(user.role === 'moderator') 
    return true;
  return false;
}

Я думаю , что вы должны изменить verify(req), admin(req)и moderator(req).

Ответил 09/10/2019 в 13:06
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more