Crear un middleware expressjs que acepte parámetros


105

Estoy intentando crear un middleware que pueda aceptar parámetros. ¿Cómo se puede hacer esto?

ejemplo

app.get('/hasToBeAdmin', HasRole('Admin'), function(req,res){

})

HasRole = function(role, req, res, next){
   if(role != user.role){
      res.redirect('/NotInRole);
   }

   next();
}

41
Ja, has hecho mi pregunta exacta para mi escenario exacto, pero 6 años antes. ASÍ que es impresionante.
Aero

6
@aero exactamente lo mismo que estaba buscando: D
Jeson Dias

4
@aero 7 años después estoy buscando exactamente el mismo: D
jean d'arme

4
@aero 7+ años después, ¡estoy buscando exactamente lo mismo!
sidd

4
@aero 8 ~ años después, ¡estoy buscando exactamente lo mismo! \ o /
Ítalo Sousa

Respuestas:


162
function HasRole(role) {
  return function(req, res, next) {
    if (role !== req.user.role) res.redirect(...);
    else next();
  }
}

También quiero asegurarme de no hacer varias copias de la misma función:

function HasRole(role) {
  return HasRole[role] || (HasRole[role] = function(req, res, next) {
    if (role !== req.user.role) res.redirect(...);
    else next();
  })
}

9
El segundo método almacena en caché los parámetros, que pueden ser o no el comportamiento deseado.
Pier-Luc Gendreau

1
@Jonathan Ong, ¿podría explicar la segunda definición de la función? ¿Lo que está sucediendo allí? No entiendo la siguiente línea return HasRole [role] || (HasRole [role] = function (req, res, next) {
Rafay Hassan

1
@JonathanOng, ¿podría explicar el segundo ejemplo un poco más para aquellos que no conocen el nodo tan bien? (incluyéndome a mí). ¿Cuándo puede obtener varias copias de la misma función y cuándo esto puede causar problemas? Gracias.
Dave

sin almacenamiento en caché, app.get('/a', hasRole('admin'))y app.get('/b', hasRole('admin'))crearía un nuevo cierre para cada uno hasRole. esto no importa demasiado de manera realista a menos que tenga una aplicación realmente grande. Solo codifico así por defecto.
Jonathan Ong

14
app.get('/hasToBeAdmin', (req, res, next) => {
  hasRole(req, res, next, 'admin');
}, (req,res) => { 
    // regular route 
});

const hasRole = (req, res, next, role) => {
   if(role != user.role){
      res.redirect('/NotInRole');
   }
   next();
};

Buena y sencilla idea. El middleware realmente es solo una función normal. ¿Por qué no pasarle otros valores? Incluya req, res y next en la primera función.
zevero

1
Si bien el código a menudo habla por sí mismo, es bueno agregar alguna explicación a su código. Esto apareció en la cola de revisión, como tienden a hacerlo las respuestas de solo código.
Will el

Estaba pensando en ese enfoque antes de visitar SO, pero pensé "Meh, seguramente hay una manera mejor". Después de visitar, veo que esta es realmente la forma más sencilla y eficaz.
Fusseldieb

3

Alternativamente, si no tiene demasiados casos o si el rol NO es una cadena:

function HasRole(role) {
  return function (req, res, next) {
    if (role !== req.user.role) res.redirect(/* ... */);
    else next();
  }
}

var middlware_hasRoleAdmin = HasRole('admin'); // define router only once

app.get('/hasToBeAdmin', middlware_hasRoleAdmin, function (req, res) {

})

solución elegante
Leos Literak

2

Si tiene varios niveles de permisos, puede estructurarlos así:

const LEVELS = Object.freeze({
  basic: 1,
  pro: 2,
  admin: 3
});

/**
 *  Check if user has the required permission level
 */
module.exports = (role) => {
  return (req, res, next) => {
    if (LEVELS[req.user.role] < LEVELS[role]) return res.status(401).end();
    return next();
  }
}

0

Yo uso esta solución. Recibo un token jwt en body req y obtengo información de rol desde allí

//roleMiddleware.js

const checkRole = role => {
    
    return (req, res, next) => {
        if (req.role == role) {
            console.log(`${role} role granted`)
            next()
        } else {
            res.status(401).send({ result: 'error', message: `No ${role} permission granted` })
        }
    }
}

module.exports = { checkRole }

Entonces, primero uso el middleware de autenticación para saber si es un usuario válido, y luego el middleware de rol para saber si el usuario tiene acceso a la ruta de la API

// router.js

router.post('/v1/something-protected', requireAuth, checkRole('commercial'), (req, res) => {
    // do what you want...
})

Espero ser de utilidad

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.