如何解决Node.js中TypeError: req.isAuthenticated is not a function错误?
问题
使用Node.js、Express、EJS、Passport.js、express-session搭建网站,配置了/admin相关路由,但访问/admin路径或相关路由时出现错误:TypeError: req.isAuthenticated is not a function。
相关代码如下:
app.js
const express = require('express') const ejsLayouts = require('express-ejs-layouts'); const cors = require('cors'); const webRoutes = require('./routes/webRoutes') const userRoutes = require('./routes/userRoutes') const app = express() app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(express.static(__dirname + '/public')); app.use(cors()); app.set('view engine', 'ejs'); app.use(ejsLayouts); // Middleware to set currentPage variable app.use(function(req, res, next) { res.locals.currentPage = req.path; next(); }); app.use("/", webRoutes); app.use("/admin", userRoutes); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log('listening on port 3000'); });
passport-config.js
const LocalStrategy = require('passport-local').Strategy const bcrypt = require('bcrypt') function initialize(passport, getUserByEmail, getUserById) { const authenticateUser = async (email, password, done) => { const user = getUserByEmail(email) if (user == null) { return done(null, false, { message: 'No user with that email' }) } try { if (await bcrypt.compare(password, user.password)) { return done(null, user) } else { return done(null, false, { message: 'Password incorrect' }) } } catch (e) { return done(e) } } passport.use(new LocalStrategy({ usernameField: 'email' }, authenticateUser)) passport.serializeUser((user, done) => done(null, user.id)) passport.deserializeUser((id, done) => { return done(null, getUserById(id)) }) } module.exports = initialize
userRoutes.js
if (process.env.NODE_ENV !== 'production') { require('dotenv').config() } const express = require('express'); const app = express(); const router = express.Router(); const bcrypt = require('bcrypt'); const passport = require('passport'); const flash = require('express-flash'); const session = require('express-session'); const methodOverride = require('method-override'); const initializePassport = require('../passport-config') initializePassport( passport, email => users.find(user => user.email === email), id => users.find(user => user.id === id) ) const users = [] app.use(flash()) app.use(session({ secret: process.env.SECRET_KEY, resave: false, saveUninitialized: false })) app.use(passport.initialize()) app.use(passport.session()) app.use(methodOverride('_method')) // Middleware to set layout for all admin routes router.use((req, res, next) => { res.locals.layout = 'layout_admin'; // Specify the admin layout next(); }); router.get('/', checkAuthenticated, (req, res) => { res.render('./admin/admin_home', {name: req.body.name}); }); router.get('/login', checkNotAutenticated, (req, res) => { res.render('./admin/admin_login'); }); router.post('/login', checkNotAutenticated, passport.authenticate('local', { successRedirect: '/', failureRedirect: 'login', failureFlash: true }) ) router.get('/register', checkNotAutenticated, (req, res) => { res.render('./admin/admin_register'); }); router.post('/register', checkNotAutenticated, async (req, res) => { try { const hashedPass = await bcrypt.hash(req.body.password, 10) users.push({ id: Date.now().toString(), name: req.body.name, email: req.body.email, password: hashedPass }) res.redirect('login') } catch (err) { res.redirect('register') } console.log(users) }); router.delete('/logout', (req, res) => { req.logOut() res.redirect('login') }) function checkAuthenticated(req, res, next) { if (req.isAuthenticated()) { return next() } res.redirect('login') } function checkNotAutenticated(req, res, next) { if (req.isAuthenticated()) { return res.redirect('/') } next() } module.exports = router;
解决方法
出现req.isAuthenticated is not a function的核心原因是:Passport和express-session的中间件没有挂载到主Express应用上,而是错误地挂载到了userRoutes.js里新建的局部app实例上,导致/admin路由无法访问到这些中间件提供的方法。
步骤1:将Passport、Session等中间件移到主app.js中
在app.js中引入并配置flash、session、passport相关中间件,确保它们在挂载路由之前执行。修改后的app.js代码如下:
const express = require('express') const ejsLayouts = require('express-ejs-layouts'); const cors = require('cors'); const flash = require('express-flash'); const session = require('express-session'); const passport = require('passport'); const methodOverride = require('method-override'); const webRoutes = require('./routes/webRoutes') const userRoutes = require('./routes/userRoutes') const app = express() // 基础中间件 app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(express.static(__dirname + '/public')); app.use(cors()); // 视图引擎配置 app.set('view engine', 'ejs'); app.use(ejsLayouts); // 认证相关中间件(必须挂载在路由之前) app.use(flash()) app.use(session({ secret: process.env.SECRET_KEY, resave: false, saveUninitialized: false })) app.use(passport.initialize()) app.use(passport.session()) app.use(methodOverride('_method')) // 初始化Passport const initializePassport = require('./passport-config') // 实际项目建议用数据库存储用户,此处为示例临时定义 const users = [] initializePassport( passport, email => users.find(user => user.email === email), id => users.find(user => user.id === id) ) // Middleware to set currentPage variable app.use(function(req, res, next) { res.locals.currentPage = req.path; next(); }); // 挂载路由 app.use("/", webRoutes); app.use("/admin", userRoutes); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log('listening on port 3000'); });
步骤2:修改userRoutes.js,移除局部app实例和重复配置
删除userRoutes.js中新建的app实例,以及flash、session、passport相关的中间件挂载代码,使用主app中初始化好的passport实例。修改后的userRoutes.js代码如下:
if (process.env.NODE_ENV !== 'production') { require('dotenv').config() } const express = require('express'); const router = express.Router(); const bcrypt = require('bcrypt'); const passport = require('passport'); // 注意:需和app.js中的users数组保持一致,实际项目用数据库 const users = [] // Middleware to set layout for all admin routes router.use((req, res, next) => { res.locals.layout = 'layout_admin'; // Specify the admin layout next(); }); router.get('/', checkAuthenticated, (req, res) => { // 登录后用户信息存储在req.user中,而非req.body res.render('./admin/admin_home', {name: req.user.name}); }); router.get('/login', checkNotAutenticated, (req, res) => { res.render('./admin/admin_login'); }); router.post('/login', checkNotAutenticated, passport.authenticate('local', { successRedirect: '/admin', // 修正跳转路径到admin首页 failureRedirect: '/admin/login', failureFlash: true }) ) router.get('/register', checkNotAutenticated, (req, res) => { res.render('./admin/admin_register'); }); router.post('/register', checkNotAutenticated, async (req, res) => { try { const hashedPass = await bcrypt.hash(req.body.password, 10) users.push({ id: Date.now().toString(), name: req.body.name, email: req.body.email, password: hashedPass }) res.redirect('/admin/login') } catch (err) { res.redirect('/admin/register') } console.log(users) }); router.delete('/logout', (req, res) => { // 新版本Passport的logOut需要回调处理错误 req.logOut(function(err) { if (err) { return next(err); } res.redirect('/admin/login') }) }) function checkAuthenticated(req, res, next) { if (req.isAuthenticated()) { return next() } res.redirect('/admin/login') } function checkNotAutenticated(req, res, next) { if (req.isAuthenticated()) { return res.redirect('/admin') } next() } module.exports = router;
额外注意点
- 用户信息获取:登录成功后,用户信息存储在
req.user中,而非req.body.name,需修正渲染数据来源。 - 路径跳转修正:确保所有/admin相关的跳转路径是完整路径,避免跳转到根路径或错误页面。
- req.logOut的回调:新版本Passport的
req.logOut必须传入回调函数处理可能的错误。 - 用户数据存储:示例中用内存数组存储用户仅作演示,实际项目需使用数据库(如MongoDB、MySQL)持久化存储。
内容的提问来源于stack exchange,提问作者Afzal Hamdulay
相关产品推荐
相关产品推荐

