Express middleware: laat alleen toegang tot admin of moderator

stemmen
0

Ik wil een route om alleen bereikbaar per moderator of admin. Ik heb geprobeerd om een middleware van het stelsel toe te passen op de route. Maar, het is gewoon ontkent toegang als men middleware niet toe te passen.
dus zeggen dat ik ben admin of moderator kan ik toegang tot /store-detail.
Maar hier, als ik ben admin Ik kan geen toegang omdat het te controleren op moderator ook.
Hier _both middlewares adminen moderatorwordt toegepast.
Ik wil dat het toepassen adminof moderator.
Hoe kan ik slechts een van hen toe te passen?
Zodat alleen admin of moderator er toegang toe.
verifymiddleware is om JWT token te controleren.
router

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

middelware

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();
  }
De vraag is gesteld op 09/10/2019 om 12:52
bron van user
In andere talen...                            


4 antwoorden

stemmen
1

Middlewares worden achtereenvolgens uitgevoerd, dus de eerste middleware die toegang weigert, stuurt een fout. Ik stel voor om een ​​enkele middleware die een parameter accepteert te maken:

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

En gebruik de middleware als volgt uit:

router.post('/store-detail', verify, hasRole(['admin', 'moderator']), async (req, res) => {})
antwoordde op 09/10/2019 om 13:09
bron van user

stemmen
0

Een JWT gebaseerde oplossing: (sorry voor de lange post, als dit ongeschikte, ik kan verwijderen)

gebruikers route (routes / users.js) Hier kom ik terug een auth-token in login operatie.

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) Hier voeg ik de rol van informatie aan de JWT bij de ondertekening van deze:

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;

middleware / auth.js Hier ben authenticiteit van de gebruiker, en het toevoegen van gedecodeerd token op het verzoek, zodat we kunnen gebruiken in de volgende middelware.

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.");
  }
};

middleware / hasRole.js Hier controleren we of de user.role voldoet aan de vereiste rollen.

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


Tot slot voegen we het volgende pad voor de gebruikers route te kunnen rol toestemming te testen.

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

Testen:

Maak 3 gebruikers afzonderlijk met de volgende met POST naar de volgende url

http: // localhost: 3000 / api / users / register

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

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

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

In MongoDB, in de collectie gebruikers updaten we rol onroerend goed voor Admin admin gebruiker en moderator voor moderator gebruiker.

Met behulp van postbode of iets dergelijks we Login met admin of moderator referenties.

In het antwoord, krijgen we de authenticatie token, gebruiken we het in de authenticatie-token header voor deze beschermde eindpunt in GET-verzoek.

http: // localhost: 3000 / api / users / me

U zult de status code 200 te krijgen.

Deze keer in te loggen we met een normale gebruikersgegevens. In het antwoord, kopiëren we de authenticatietoken, en deze gebruiken in auth-token header voor deze beschermde eindpunt in GET-verzoek.

http: // localhost: 3000 / api / users / me

U krijgt de status code 403 verboden.

antwoordde op 09/10/2019 om 14:04
bron van user

stemmen
0

irrelevant deel

Van systeemarchitectuur oogpunt, hier is wat ik ga doen.

  1. In plaats van het maken van het netwerk verzoek binnen de middleware everytime, slaan de gebruiker toegang niveau in een token (zeg JWT token).

  2. Op het moment dat de gebruiker inloggen op het systeem, een verzoek aan de backend, zal de backend maken verzoek aan dB tot de gegevens (die zou moeten overwegen de toegang niveau) en coderen van de gebruiker gegevens in token retrive. Uw token moet het toegangsniveau bevatten.

  3. Op elk verzoek, zal je token in de headers, de-crypt het teken hebben, en stel de details van de toegang niveaus (res.locals zou de ideale plek om tokens te stellen zijn).

   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.`)
        }
    }

relevante deel

Sinds het instellen van res.status(403).send({error: { status:403, message:'Access denied.'}});het zinvol is de reden waarom het niet zal verplaatsen naar de volgende middleware.

Omdat voor een API-oproep kunt u *Probablyheaders eenmaal sturen Zoals gesuggereerd door Jérémie L maken van een single-nieuwe middleware zou de beste aanpak

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();
  }
}
antwoordde op 09/10/2019 om 13:59
bron van user

stemmen
0

Maak een nieuwe functie, bv isAuthorizedom die logica hanteren.

Als u belt [verify, admin, moderator]afzonderlijk ze niet van elkaar afhankelijk.

route:

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.'}});
}

middleware:

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

Ik denk dat je aan te passen verify(req), admin(req)en moderator(req).

antwoordde op 09/10/2019 om 13:06
bron van user

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