这是我第一次尝试 PassportJS,我正在关注他们的文档,这对我来说很难理解。
我的目标是让用户通过注册页面(一个反应组件)注册,然后使用登录页面(也是一个反应组件)登录并重定向到仪表板(这也是一个反应组件)。
以上所有都可以,但没有护照。我不确定我做错了什么,因为这是我第一次使用它,但它似乎不起作用。不工作是指我没有看到任何错误消息,并且登录页面没有重定向到仪表板。
虽然我已经几乎把所有东西放在一起了,但不知怎的,我要么遗漏了一些东西,要么做错了一些事情。
这是我的 React Login 组件,它正在向 API 发送 Axios post 请求:
const onSubmit = (e) => {
e.preventDefault();
const newLogin = {
username,
password,
};
const config = {
headers: {
'Content-Type': 'application/JSON',
},
};
axios.post('/api/v1/login', newLogin, config).then((res) => {
return res.data === 'user'
? (window.location = '/dashboard')
: (window.location = '/login');
});
setUsername('');
setPassword('');
};
在 Node server.js 上,上述登录 post 请求通过路由器发送到控制器。
服务器.js:
app.use(passport.initialize());
app.use(passport.session());
app.use('/api/v1/register', register);
app.use('/api/v1/login', login);
Router.js(登录):
const { loginUser } = require('../controllers/loginController');
router.route('/').post(loginUser);
loginController.js:
require('../config/passport')(passport);
exports.loginUser = (req, res, next) => {
passport.authenticate('local', {
successRedirect: '/dashboard',
failureRedirect: '/register',
failureFlash: true,
});
};
这是我的passport.js 文件:
const User = require('../models/UserModel');
const passport = require('passport');
module.exports = function (passport) {
passport.use(
new LocalStrategy((username, password, done) => {
// Match User
User.findOne({ username: username })
.then((err, user) => {
if (!user) {
return done(null, false, console.log('no user'));
}
//Match Password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Password incorrect' });
}
});
})
.catch((err) => console.log(err));
})
);
};
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findOne(id, (err, user) => {
done(err, user);
});
});
我建议从护照登录控制器中删除重定向,并根据应用程序是否在响应中收到用户来在内部处理来自反应应用程序的路由(通过反应路由器或其他方式)。这是我在最近的项目中设置它的方法:
//router.js
const express = require("express");
const fs = require("fs");
const { userModel } = require("../models");
const passport = require("passport");
const bcrypt = require("bcrypt");
const bodyParser = express.urlencoded({ extended: true, limit: "50mb" });
const jsonParser = express.json({ limit: "50mb" });
const router = express.Router();
const saltRounds = 10;
router.get("/login", (req, res) => {
res.render("login");
});
router.get("/logout", (req, res, next) => {
req.logout();
req.session.destroy();
next();
});
router.post("/login", bodyParser, jsonParser, **passport.authenticate("local"),** (req, res, next) => {
if (!req.user) return;
const date = new Date();
console.log(`User ID:${req.user._id} logged in at ${date}`);
res.json(req.user);
next();
});
module.exports = router;
注意我如何使用护照作为中间件,然后继续执行下一个函数,该函数检查应该从上一个函数接收到的用户
确保您设置了正确的参数。 以下是护照配置示例。
const LocalStrategy = require("passport-local").Strategy;
const passport = require('passport');
const session = require('express-session');
const bcrypt = require("bcryptjs");
const User = require("./../model/user/User");
function setUpPassportConfig(app) {
const authenticateUser = async (email, password, done) => {
try {
const user = await User.findOne({ email });
if (!user) {
return done(null, false, { message: 'No user with that email' });
}
const isMatch = await bcrypt.compare(password, user.password);
if (isMatch) {
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) => {
User.findById(id).then(user => {
done(null, user);
}).catch(err => {
done(err);
});
});
app.use(session({
secret: 'secret', // Replace with a secure, randomly generated string in production
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: false,
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days in milliseconds,
rolling: true, // This resets the cookie's maxAge countdown with each request.
// sameSite: true
}
}));
app.use(passport.initialize());
app.use(passport.session());
}
module.exports = setUpPassportConfig;
确保您已允许 cors。另外,如果您在本地使用它,请使用 IP 地址而不是 localhost。
app.use(cors({
origin: 'http://127.0.0.1:3000', // Replace with the URL of your front-end app
credentials: true, // This is important for cookies, authorization headers with HTTPS
}))
在您的 React 应用程序中,确保将 withCredentials 属性传递为 true。 这将确保会话通过客户端的 cookie 进行工作。 如果您使用的是 axios 拦截器,只需使用
request.withCredentials =true;