【问题标题】:How to handle errors with Express-JWT如何使用 Express-JWT 处理错误
【发布时间】:2017-04-19 14:44:46
【问题描述】:

我正在尝试合并 express-jwt 库,但我不太明白它的错误处理是如何工作的。

documentation 说:

错误处理

默认行为是在令牌无效时抛出错误,因此您可以>添加自定义逻辑来管理未经授权的访问,如下所示:

    app.use(function (err, req, res, next) {
      if (err.name === 'UnauthorizedError') {
        res.status(401).send('invalid token...');
      }
    });

但我很困惑它是如何工作的。如果我有一个简单的reqres 情况,并且如果令牌有效,我想调用next,或者如果不是,则调用next 并出现错误,我将app.use 函数放在哪里?

例如,这是我的代码:

router.post('/', expressJwt({  
  secret: jwtSecret,     
  credentialsRequired: false  
}), (req, res, next) => {   
  databaseController.findUser(req.user.email, (err, user) => {          
    if (err) {          
      return next(err)      
    }                        
    res.json(user)     
  })         
})

这里的err 来自我的数据库调用,而不是来自 express-jwt 验证。 任何帮助表示赞赏。

【问题讨论】:

  • 如果你查看express-jwtgithub.com/auth0/express-jwt/blob/master/lib/index.js#L51的代码,你会发现它使用next(err)next将请求传递给错误处理程序或请求处理程序。
  • 所以我不清楚,next 是在哪里传递给expressJwt 函数的?
  • 在 L33 上,middleware 函数具有签名 function(req, res, next)。在 L130 上,返回 middleware。因此,当您在路由器中传递expressJwt({}) 时,它会返回一个function(req, res, next),它接受快递reqresnext。希望你很清楚:)

标签: node.js express jwt express-jwt


【解决方案1】:
import { Schema, model } from "mongoose";

export const ROLES = ["Admin", "Estudiante","Docente","Secretario","Vicerrector","Inpector"];

const roleSchema = new Schema(
  {
    name: String,
  },
  {
    versionKey: false,
  }
);

export default model("Role", roleSchema);


//
import { Schema, model } from "mongoose";
import bcrypt from "bcryptjs";

const productSchema = new Schema(
  {
    username: {
      type: String,
      unique: true,
    },
    email: {
      type: String,
      unique: true,
    },
    password: {
      type: String,
      required: true,
    },
    //********************************NUEVOS CAMPOS PARA USUARIOS ADMINISTRADORES
    nombres: {
      type: String,
      required: true,
    },
    apellidos: {
      type: String,
      required: true,
    },
    cedula: {
      type: String,
      unique: true,
    },
    foto: {
      type: String,
      required: true,
    },
    status: {
      type: String,
      required: true,
    },
    telefono: {
      type: String,
      required: true,
    },
    //---------------TIPO DE DOCUMENTOS
    typo:{
      type: String,
    },
    //---------------TIPO MAS DATOS
    roles: [
      {
        type: Schema.Types.ObjectId,
        ref: "Role",
      },
    ],
  },
  {
    timestamps: true,
    versionKey: false,
  }
);

productSchema.statics.encryptPassword = async (password) => {
  const salt = await bcrypt.genSalt(10);
  return await bcrypt.hash(password, salt);
};

productSchema.statics.comparePassword = async (password, receivedPassword) => {
  return await bcrypt.compare(password, receivedPassword)
}

export default model("User", productSchema);

//
import Role from "../models/Role";
import User from "../models/User";

import bcrypt from "bcryptjs";

export const createRoles = async () => {
  try {
    // Count Documents
    const count = await Role.estimatedDocumentCount();

    // check for existing roles
    if (count > 0) return;

    // Create default Roles
    const values = await Promise.all([
      new Role({ name: "Estudiante" }).save(),//user
      new Role({ name: "Docente" }).save(),//moderator
      new Role({ name: "Admin" }).save(),//admin
      new Role({ name: "Secretario" }).save(),//-------+++
      new Role({ name: "Vicerrector" }).save(),//-------+++
      new Role({ name: "Inpector" }).save(),//-------+++
    ]);

    console.log(values);
  } catch (error) {
    console.error(error);
  }
};

export const createAdmin = async () => {
  // check for an existing admin user
  const user = await User.findOne({ email: "10004095632w@gmailcom" });
  // get roles _id
  const roles = await Role.find({ name: { $in: ["Admin", "Estudiante","Docente","Secretario","Vicerrector","Inpector"] } });

  if (!user) {
    // create a new admin user
    await User.create({
      username: "admin",
      email: "10004095632w@gmail.com",
      password: await bcrypt.hash("Imperio 789.", 10),
      roles: roles.map((role) => role._id),
      nombres: "ad",
      apellidos: "ad",
      cedula: "123456789",
      foto: "profile.jpg",
      status: "Activo",
      telefono: "+570995283857",
    });
    console.log('Admin User Created!')
  }
};

//
import jwt from "jsonwebtoken";
import config from "../config";
import User from "../models/User";
import Role from "../models/Role";

export const verifyToken = async (req, res, next) => {
  let token = req.headers["x-access-token"];

  if (!token) return res.status(403).json({ message: "No token provided" });

  try {
    const decoded = jwt.verify(token, config.SECRET);
    req.userId = decoded.id;

    const user = await User.findById(req.userId, { password: 0 });
    if (!user) return res.status(404).json({ message: "No user found" });

    next();
  } catch (error) {
    return res.status(401).json({ message: "Unauthorized!" });
  }
};

export const isSecretario = async (req, res, next) => {
  try {
    const user = await User.findById(req.userId);
    const roles = await Role.find({ _id: { $in: user.roles } });

    for (let i = 0; i < roles.length; i++) {
      if (roles[i].name === "Secretario") {
        next();
        return;
      }
    }

    return res.status(403).json({ message: "Require Moderator Role!" });
  } catch (error) {
    console.log(error)
    return res.status(500).send({ message: error });
  }
};

export const isAdmin = async (req, res, next) => {
  try {
    const user = await User.findById(req.userId);
    const roles = await Role.find({ _id: { $in: user.roles } });

    for (let i = 0; i < roles.length; i++) {
      if (roles[i].name === "Admin"||roles[i].name === "Secretario") {
        next();
        return;
      }
    }

    return res.status(403).json({ message: "Require Admin Role!" });
  } catch (error) {
    console.log(error)
    return res.status(500).send({ message: error });
  }
};

//

import User from "../models/User";
import { ROLES } from "../models/Role";

const checkDuplicateUsernameOrEmail = async (req, res, next) => {
  try {
    const user = await User.findOne({ username: req.body.username });
    if (user)
      return res.status(400).json({ message: "El numero de cédula ya existe" });
    const email = await User.findOne({ email: req.body.email });
    if (email)
      return res.status(400).json({ message: "El correo electrónico ya existe" });
    next();
  } catch (error) {
    res.status(500).json({ message: error });
  }
};

const checkRolesExisted = (req, res, next) => {
  if (req.body.roles) {
    for (let i = 0; i < req.body.roles.length; i++) {
      if (!ROLES.includes(req.body.roles[i])) {
        return res.status(400).json({
          message: `Role ${req.body.roles[i]} does not exist`,
        });
      }
    }
  }

  next();
};

export { checkDuplicateUsernameOrEmail, checkRolesExisted };

//
import * as authJwt from "./authJwt";
import * as verifySignup from "./verifySignup";

export { authJwt, verifySignup };

【讨论】:

    【解决方案2】:

    您可以在用于启动 express 服务器的代码之前创建一个 express 中间件。

    // Global error handler that takes 4 arguments and ExpressJS knows that
    app.use((err, req, res, next) => {
        res.status(err.status).json(err);
    });
    app.listen(3000);
    

    我将它用于使用 REST 的应用程序,但您可以使用相同的方法并根据您的需要修改应该发生的事情。例如,如果您使用 Jade 模板,那么您需要使用要向最终用户显示的数据填充模板并将其余部分记录在您的日志文件中。

    app.use((err, req, res, next) => {
        res.locals.status = status;
        res.render('error')
    });
    

    【讨论】:

      【解决方案3】:

      这是我针对个别路线的解决方案。

      function UseJwt(){
          return [
              jwtExpress({ secret: configuration.jwtSecret, algorithms: ['HS256'] }),
              function(err, req, res, next){
                  res.status(err.status).json(err);
              }
          ]
      }
      

      用法...

      app.get(`/${prefix}/:user_id`,
              ...UseJwt(),
              async function (req, res) {           
                 // handle your code here.
              }
      )
      

      【讨论】:

      • 这是个人路线的最佳答案!非常感谢!
      【解决方案4】:

      另一种方法是,您可以将中间件与 app.use 一起放置,以扫描所有路由以在标头或查询字符串中找到有效的 jwt。 使用 unless 关键字可以免除任何公共端点。 例如:

      app.use(expressjwt({credentialsRequired: true, secret: config.TOKEN_SECRET, requestProperty: 'user'}).unless({path: config.PUBLIC_URLs}));
      
      app.use(function(err, req, res, next) {
          if(err.name === 'UnauthorizedError') {
            res.status(err.status).send({message:err.message});
            logger.error(err);
            return;
          }
       next();
      });
      

      【讨论】:

      • 完美运行,这应该是公认的答案
      • 另请注意,您必须将 next 作为该函数的参数,否则将不会执行。
      • @magician11 确实有 next() 作为参数。除了这个,你还有什么意思?
      • 错误函数内部的最后一次调用不应该是next(err)吗?
      • 我只想提一下,如果您只想在一条路线中包含 expressJwt 而不是全部。我把 app.use() 错误函数放在我使用 expressJwt 的路由之后。
      猜你喜欢
      • 2021-08-05
      • 2012-03-02
      • 2016-10-08
      • 2019-10-10
      • 2023-01-05
      • 2021-07-29
      • 2016-06-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多