我正在使用 PERN 堆栈构建一个全栈应用程序。我可以从前端
POST
输入用户名和密码到/login
并成功登录 - isAuthenticated()
返回true。但是,如果我随后向 GET
发送 /current-session
请求,isAuthenticated()
将返回 false。
我想知道哪里可能出了问题。我已经查看了 Stack Overflow 上可以找到的所有相关问题,但尚未找到答案。
服务器正在运行。数据库连接建立,对数据库进行查询工作。正在填充数据库表“会话”。例如:
希德 | sess | 过期 |
---|---|---|
T6akfD8h0TWysQD2jZ5LqIBlxCBIcLSp | [对象对象] | 2024-03-06 00:54:22 +0000 |
在浏览器中,当我
POST
到/login
时,我可以从响应标头中看到 sid 作为仅 HTTP 的 cookie。例如:
connect.sid=s%3AT6akfD8h0TWysQD2jZ5LqIBlxCBIcLSp.H01oGHOfFy9m6Tt451i%2F0vpoKuYEDPN8insL7Wx8GaU;
Path=/;
Expires=Wed, 06 Mar 2024 00:54:21 GMT;
HttpOnly
但是,当我
GET
到/current-session
时,我在浏览器响应中看不到cookie。
似乎
app.use(passport.session());
出于某种原因告诉app.get("/current-session"...
没有当前用户会话/当前经过身份验证的用户。想知道会话是否由于某种原因没有被维持......?
从我的日志中,我可以看到
passport.deserializeUser()
从未被调用过。
这是当前代码:
const express = require("express");
const session = require("express-session");
const passport = require("passport");
const LocalStrategy = require("passport-local");
const bcrypt = require("bcrypt");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const { Pool } = require("pg");
const pgSession = require("connect-pg-simple")(session);
require("dotenv").config();
// ------------- GENERAL SETUP --------------- //
const app = express();
const PORT = process.env.PORT || 3001;
app.use(
cors({
origin: "https://nightlifeapp.onrender.com", // "http://localhost:5173",
credentials: true,
"Access-Control-Allow-Credentials": true,
})
);
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
// ----------- DATABASE CONNECTION ----------- //
// Create a PostgreSQL connection pool
const pool = new Pool({
connectionString: process.env.ELEPHANTSQL_CONNECTION_URL,
max: 5,
});
// Test the database connection
pool.connect((err, client, done) => {
if (err) {
console.error("Error connecting to the database", err);
} else {
console.log("Connected to the database");
}
});
// ----------- PASSPORT STRATEGIES ----------- //
passport.use(
"local",
new LocalStrategy((username, password, done) => {
// Query the PostgreSQL database to find a user by username
pool.query(
"SELECT * FROM users WHERE username = $1",
[username],
(err, result) => {
if (err) {
return done(err);
}
// Check if the user exists
const user = result.rows[0];
if (!user) {
return done(null, false);
}
// Check if the password is correct
if (!bcrypt.compareSync(password, user.password_hash)) {
return done(null, false);
}
// If the username and password are correct, return the user
return done(null, user);
}
);
})
);
passport.serializeUser((user, done) => {
console.log("serializeUser invoked");
done(null, user);
});
passport.deserializeUser((user, done) => {
console.log("serializeUser invoked");
done(null, user);
});
// ------------- EXPRESS SESSION ------------- //
app.use(
session({
store: new pgSession({
pool,
tableName: "session",
}),
secret: process.env.EXPRESS_SESSION_SECRET_KEY,
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 14 * 24 * 60 * 60 * 1000, // 14 days session timeout
},
})
);
app.use(passport.initialize());
app.use(passport.session());
// ----------------- ROUTING ----------------- //
app.get("/current-session", passport.authenticate("session"), (req, res) => {
if (req.isAuthenticated()) {
console.log("At GET /current-session... isAuthenticated() returned true");
} else {
console.log("At GET /current-session... isAuthenticated() returned false");
}
if (!req.user) {
res.json({ currentlyLoggedIn: false });
} else {
res.json({
currentlyLoggedIn: true,
userId: req.user.user_id,
username: req.user.username,
});
}
});
app.post(
"/login",
passport.authenticate("local"), (req, res) => {
if (req.isAuthenticated()) {
console.log("At POST /login... isAuthenticated() returned true");
} else {
console.log("At POST /login... isAuthenticated() returned false");
}
return res.json({
loginSuccessful: true,
userId: req.user.user_id,
username: req.user.username,
});
}
);
// ------------------ SERVER ----------------- //
const server = app.listen(PORT, () =>
console.log(`Server is listening on port ${PORT}`)
);
server.keepAliveTimeout = 120 * 1000;
server.headersTimeout = 120 * 1000;
在前端,我向
/login
和 /current-session
发出的获取请求如下:
fetch(`${URL}/login`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "https://nightlifeapp.onrender.com",
},
body: jsonData,
})
还有
fetch(`${URL}/current-session`, {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "https://nightlifeapp.onrender.com",
},
})```
Any suggestions would be greatly appreciated!
所以,事实证明,这里的问题是 cookie 被阻止,因为前端和后端托管在不同的 Render.com 子域上,因此 cookie 没有被用来保持会话继续进行.
我可以使用不同/自定义域名来避免 cookie 被阻止。
相反,我提出的解决方案是将前端静态文件托管在与 Express API 端点相同的服务器上。为此,我将 React 前端的 build/dist 文件放置在 Express 服务器上的一个文件夹中,然后使用
app.use(express.static("dist"));
为前端 index.html
文件提供服务 - 默认情况下调用 '/ ' 终点。