Express.js:使用 Passport.js 实现基于角色的身份验证时出现过多重定向问题

问题描述 投票:0回答:1

在实现基于角色的身份验证时遇到 Express.js 和 Passport.js 问题。尽管设置了特定于角色的中间件,但我仍面临“重定向过多”错误。该应用程序涉及根据用户的角色(例如管理员、招聘人员)将用户重定向到各自的仪表板。如何解决此重定向循环并确保正确的身份验证流程?

我的尝试:我使用 Passport.js 在 Express.js 应用程序中实现了基于角色的身份验证。我定义了中间件函数来检查用户是否经过身份验证,并根据用户的角色(招聘者、用户或管理员)将他们重定向到各自的仪表板。我还配置了 Passport.js 以根据用户在登录期间的角色对用户进行身份验证。

passport.config.js 文件:

const LocalStrategy = require("passport-local").Strategy;
const db = require("./db");
const bCrypt = require("bcrypt");

async function initializePassport(passport) {
  async function authUser(email, password, formUserType, done) {
    try {
      let userType;
      switch (formUserType) {
        case "recruteur":
          userType = "recruteur";
          break;
        case "users":
          userType = "users";
          break;
        case "admin":
          userType = "admin";
          break;
        default:
          throw new Error("this user type does not exist");
      }

      const query = `SELECT * FROM ${userType} WHERE email = $1`;
      const { rows } = await db.query(query, [email]);
      const user = rows[0];

      if (!user) {
        return done(null, false, { message: "Incorrect email address" });
      }

      const passwordMatched = await bCrypt.compare(password, user.mot_de_passe);
      if (!passwordMatched) {
        return done(null, false, { message: "Incorrect password" });
      }

      user.userType = userType;
      return done(null, user, userType);
    } catch (error) {
      done(error);
    }
  }

  passport.use(
    new LocalStrategy(
      {
        usernameField: "email",
        passReqToCallback: true,
      },
      async (req, email, password, done) => {
        await authUser(email, password, req.body.userType, done);
      }
    )
  );

  passport.serializeUser((user, done) => {
    let userId;
    let userType;
    switch (user.userType) {
      case "recruteur":
        userId = user.recruteur_id;
        userType = "recruteur";
        break;
      case "users":
        userId = user.users_id;
        userType = "users";
        break;
      case "admin":
        userId = user.admin_id;
        userType = "admin";
        break;
      default:
        return done(new Error("Unknown user type"));
    }

   
    
    done(null, { id: userId, type: userType });
  });

  passport.deserializeUser(async (idObj, done) => {
    try {
      const { id, type } = idObj;
      let userType;
      switch (type) {
        case "recruteur":
          userType = "recruteur";
          break;
        case "users":
          userType = "users";
          break;
        case "admin":
          userType = "admin";
          break;
        default:
          return done(new Error("Unknown user type"));
      }
  
      const query = `SELECT * FROM ${userType} WHERE ${userType}_id = $1`;
      const { rows } = await db.query(query, [id]);
      const user = rows[0];
  
      if (!user) {
        return done(new Error("User not found"));
      }

      user.userType = userType;
      return done(null, user, userType);
    } catch (err) {
      done(err);
    }
  });
  
}

module.exports = initializePassport;

app.js 文件与我的中间件 checkAuthenticated、checkNotAuthenticated

if (process.env.NODE_ENV !== "production") {
  require("dotenv").config();
}
const initializePassport = require("./config/passport.config");

const express = require("express");
const passport = require("passport");
const session = require("express-session");

const path = require("path");
const helmet = require("helmet");
const flash = require("express-flash");
const methodOverride = require('method-override');


const logger = require("morgan");

const app = express();
app.use(express.urlencoded({ extended: false }));
app.use(flash());

//initialise user session
app.use(
  session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: true,
    cookie: {
      maxAge: 3 * 60 * 60 * 1000,
      httpOnly: true 
    }
  })
);

app.use(passport.session() );
app.use(passport.initialize());


app.use(methodOverride('_method'))

//auth the user in the DB
initializePassport(passport);

const checkAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    const userType = req.user.userType;
    console.log('Role --------------------------------------------------->',userType);
    if (userType === 'recruteur') {
      return res.redirect("/recruter/dashboard");
    } else if (userType === 'users') {
      return res.redirect("/users/dashboard");
    } else if (userType === 'admin') {
      return res.redirect("/admin/dashboard");
    } else {
      return res.redirect("/login");
    }
  } else {
    return res.redirect("/login");
  }
};





const checkNotAuthenticated = (req, res, next) => {
  if (!req.isAuthenticated()) {
    return next(); 
  } else {
    const userType = req.user.userType;
    switch (userType) {
      case 'recruteur':
        return res.redirect("/recruter/dashboard");
      case 'users':
        return res.redirect("/users/dashboard");
      case 'admin':
        return res.redirect("/admin/dashboard");
      default:
        return res.redirect("/login");
    }
  }
};

const staticFilesPath = path.join(__dirname, 'views');

app.use(helmet());
app.use(express.json());

app.use(express.static(path.join(__dirname, "views")));
app.use("views", express.static(path.join(__dirname, "views", "public")));
app.use(express.static(staticFilesPath, { type: 'application/javascript' }));


app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));

app.use(logger("tiny"));

module.exports = { app, checkAuthenticated, checkNotAuthenticated };

server.js 文件:

if (process.env.NODE_ENV !== 'production') {
  require("dotenv").config();
}

const offersRoute = require("./routes/offers/offers.route");
const homeRouter = require('./routes/home/home.route');
const offerRoute = require('./routes/offers/offer.route');
const authRoute = require('./routes/auth/auth.route');
const recruterRoute = require('./routes/recruter/recruter.route');
const usersRoute = require('./routes/users/users.route');
const unauthorizedRoute = require('./routes/auth/unauthorized.route');

const http = require("http");


const { app } = require("./app");

const PORT = process.env.PORT;

const server = http.createServer(app);

app.use('/home', homeRouter); 
app.use("/offers", offersRoute);
app.use("/offer", offerRoute);
app.use("/login", authRoute);
app.use("/recruter", recruterRoute);
app.use('/users', usersRoute);
app.use('/unauthorized', unauthorizedRoute)

server.listen(PORT, () => {
  console.log(`You are listening to port ${PORT}...`);
});

auth.route.js 文件:

const express = require("express");
const passport = require("passport");
const flash = require("express-flash");

const authRoute = express.Router();
const {
  postNewRecruterAuth,
  validate,
  recruterAuthValidationRule,
  postNewUserAuth,
  userAuthValidationRule,
} = require("../../controllers/auth.controller");
const { checkAuthenticated, checkNotAuthenticated } = require("../../app");

//User render login routes
authRoute.get("/", (req, res) => {
  res.render("auth/users/login", {
    title: "Connectez vous.",
  });
});

//User register routes
authRoute.get("/register", (req, res) => {
  res.render("auth/users/register", {
    title: "Créer un compte utilisateur",
  });
});

//User authentification routes
authRoute.post(
  "/",
  passport.authenticate("local", {
    successRedirect: "/users/dashboard",
    failureRedirect: "/login",
    failureFlash: true,
  })
);


//user registration route
authRoute.post(
  "/register",
  userAuthValidationRule(),
  validate,
  postNewUserAuth
);




//Recruter Auth routes
authRoute.get("/recruter", checkNotAuthenticated, (req, res) => {
  //recruter login
  res.render("auth/recruter/recruter_login", {
    title: "Connectez vous en tant que recruteur.",
    messages: req.flash("error"),
  });
});

//recruter login route
authRoute.post(
  "/recruter",
  passport.authenticate("local", {
    successRedirect: "/recruter/dashboard",
    failureRedirect: "/login/recruter",
    failureFlash: true,
  })
);

authRoute.get("/recruter/register", checkNotAuthenticated, (req, res) => {
  //recruter register
  res.render("auth/recruter/recruter_register", {
    title: "Créer un compte recruteur",
  });
});

authRoute.post(
  "/recruter/register",
  recruterAuthValidationRule(),
  validate,
  postNewRecruterAuth
);

//Admin Auth routes

module.exports = authRoute;

目前,我正在 Express.js 应用程序中专门为招聘人员测试登录功能。但是,当 recruteur 成功登录时,应用程序不会被重定向到 recruteur 仪表板(“/recruter/dashboard”),而是进入无限重定向循环。

我实际正在测试的

recruter.route.js 文件:

const express = require("express");
const recruterRoute = express.Router();
const passport = require("passport");
const { checkAuthenticated } = require("../../app");

recruterRoute.get("/", (req, res) => {
  res.render("layouts/recruter/recruter_page", {
    title: "Jobify pour recruteur",
  });
});

recruterRoute.post("/dashboard/logout", (req, res) => {
  req.logout(() => {
    res.redirect("/login/recruter");
  });
});

recruterRoute.get("/dashboard", checkAuthenticated,   (req, res) => {
  res.render("layouts/recruter/recruter_dashboard", {
    title: "Votre tableau de bord",
    user: req.user,
  });
});

module.exports = recruterRoute;

我的期望:我期望当用户登录时,他们会根据其角色被重定向到适当的仪表板。例如,招聘人员应重定向到“/recruter/dashboard”,用户应重定向到“/users/dashboard”,管理员应重定向到“/admin/dashboard”。我还期望身份验证过程能够无缝运行,没有任何重定向循环。

实际结果:但是,当我测试该应用程序时,我遇到了“太多重定向”错误。似乎在身份验证过程中的某个地方发生了重定向循环,但我不确定问题出在哪里。尽管设置了特定于角色的中间件并配置了 Passport.js 来处理基于角色的身份验证,但重定向循环仍然存在,并且我无法解决它。

这是我在日志中得到的无限循环:

GET /login/recruter 200 14109 - 9.306 ms
POST /login/recruter 302 82 - 104.179 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.246 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.197 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.033 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.213 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.997 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.092 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.248 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.335 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.232 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.296 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.186 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.198 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.775 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.093 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.145 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.099 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.038 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.684 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.291 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.575 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.015 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.837 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.956 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.713 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.973 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.684 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.953 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.527 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.906 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.907 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 0.629 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.024 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 3.310 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.657 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.030 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.575 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.036 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.140 ms
Role ---------------------------------------------------> recruteur
GET /recruter/dashboard 302 82 - 1.440 ms
javascript node.js express backend
1个回答
0
投票

问题在于,位于所有需要身份验证的路由处理程序前面的

checkAuthenticated
中间件永远不会调用
next()
来允许处理程序实际处理请求。相反,它始终执行重定向。

您似乎正在尝试实施访问控制角色。我建议像这样更改您的身份验证中间件:

const checkAuthenticated = (requiredRole) => (req, res, next) => {
  if (!req.isAuthenticated()) {
    return res.redirect("/login");
  }

  const userType = req.user.userType;
  if (userType !== requiredRole) {
    return redirectToRoleHomePage(req, res);
  }

  // user is authenticated and has the correct role,
  // pass control to the handler from this middleware
  return next();
};

const checkNotAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return redirectToRoleHomePage(req, res);
  }

  // user is *not* authenticated, pass control to the handler
  return next();
};

const redirectToRoleHomePage = (req, res) => {
  const userType = req.user.userType;
  switch (userType) {
    case "recruteur":
      return res.redirect("/recruter/dashboard");
    case "users":
      return res.redirect("/users/dashboard");
    case "admin":
      return res.redirect("/admin/dashboard");
    default:
      return res.redirect("/login");
  }
};

现在,如果不满足条件,

check*
中间件将重定向,否则调用
next()
。我已将
checkAuthenticated
更改为采用
requiredRole
参数 - 这允许您在处理程序级别指定该处理程序需要哪个角色。有了这个,您可以更改您的处理程序,例如:

recruterRoute.get("/", (req, res) => {
  res.render("layouts/recruter/recruter_page", {
    title: "Jobify pour recruteur",
  });
});

recruterRoute.post("/dashboard/logout", checkAuthenticated('recruteur'), (req, res) => {
  req.logout(() => {
    res.redirect("/login/recruter");
  });
});

recruterRoute.get("/dashboard", checkAuthenticated('recruteur'), (req, res) => {
  res.render("layouts/recruter/recruter_dashboard", {
    title: "Votre tableau de bord",
    user: req.user,
  });
});

对于其他角色,您可以使用

checkAuthenticated('users')
checkAuthenticated('admin')
等。

或者,如果您希望 recruterRoute 内的

all
路由检查用户是否已登录并且是
recruteur
,您可以只在
recruterRoute
路由器本身中应用中间件,这样您就不需要记住每条路线:

const recruterRoute = express.Router();

recruterRoute.use(checkAuthenticated('recruteur'));
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// middleware applied with `.use()` before the routes
// -> gets called before any route below

recruterRoute.get("/", (req, res) => {
  res.render("layouts/recruter/recruter_page", {
    title: "Jobify pour recruteur",
  });
});

recruterRoute.post("/dashboard/logout", (req, res) => {
  req.logout(() => {
    res.redirect("/login/recruter");
  });
});

recruterRoute.get("/dashboard", (req, res) => {
  res.render("layouts/recruter/recruter_dashboard", {
    title: "Votre tableau de bord",
    user: req.user,
  });
});
© www.soinside.com 2019 - 2024. All rights reserved.