express-session 每次都设置新的 session,并且在创建 session 后不会持续存在
express-session setting new session every time, and does not persist after creating the session
- 我在 localhost:8080
上为我的后端使用 express
- 在 localhost:3000
上为我的前端使用 React
- 没有使用代理。只是简单的 http://localhost:3000/roster 发送一个请求到 http://localhost:8080/
- 我也为 cors() 设置了所有交叉原点和 header 东西
- 我的 postgres 存储已设置,当我查询 sessions table 时,我可以在那里看到 session 数据。
- 在我的前端使用 axios 进行抓取,我也在那里配置了
credentials:true
。
我在控制台中将我的 sessionID 记录在中间件中(在设置之前),然后我在用于测试我的 session 的请求中再次记录它。在中间件中,它按预期无法识别,然后一旦我将其记录在请求中,我就会按预期获得真实的 session id。一切似乎都正常,但是当我第二次发送请求时,它发回了一个全新的 sessionID 而不是保留。
当我进一步查看时,它说它可能与未设置 cookie 有关?但我已经仔细检查了 resave
和 httpOnly
等正确配置设置,所有这些似乎也都还可以。
我做错了什么,我该如何解决?我首先在此处包含了客户端和服务器的 package.json....
PACKAGE.JSON 客户
{
"name": "client.react",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.21.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "^5.2.0",
"react-scripts": "0.9.5"
},
"devDependencies": {},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
PACKAGE.JSON 服务器
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.19.0",
"connect-pg-simple": "^6.2.1",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"express-session": "^1.17.1",
"jsonwebtoken": "^8.5.1",
"pg": "^8.5.1",
"uuid": "^8.3.2"
}
}
客户端
/**
* File: /src/pages/roster.js
* Date: 01-28-2021
* Author: jreyes
*
* Date | Author | Change
* ---------------------------------------------------------------------------------------
* 01-29-2021 | jreyes | initialization
* ---------------------------------------------------------------------------------------
*/
import React from 'react';
import Axios from 'axios';
const Roster = () => {
const [roster, setRoster] = React.useState([]);
/** Fetch the roster. */
const handleRoster = () => {
Axios.get("http://localhost:8080/", {
headers: {
"Content-Type":"application/json"
}
}, { withCredentials: true })
.then((response) => {
console.log(response.data);
})
}
return (
<React.Fragment>
<h1>Roster</h1>
<button onClick={handleRoster}>Get Roster</button>
</React.Fragment>
)
}
export default Roster;
来自 CHROME
的客户端控制台日志
注意从服务器发回的两个不同的 sessionID。在同一个花名册页面上,我只需单击一次按钮,然后再单击一次。
{hit: "!/", session: "SessionID: db9af88c-0101-4bf5-82c7-f57fbe9dac1d"}
hit: "!/"
session: "SessionID: db9af88c-0101-4bf5-82c7-f57fbe9dac1d"
__proto__: Object
roster.js:25
{hit: "!/", session: "SessionID: b1a5ffd2-c986-4932-827c-a6ce644a0b3e"}
hit: "!/"
session: "SessionID: b1a5ffd2-c986-4932-827c-a6ce644a0b3e"
__proto__: Object
服务器
/**
* File: index.js
* Date: 01-20-2021
* Author: Bennm23
*
* Date | Author | Change
* ---------------------------------------------------------------------------------------
* 01-20-2021 | benm23 | initialization
* ---------------------------------------------------------------------------------------
* 01-29-2021 | jreyes | formatted code; fixed cors; added env
* | | functionality; db is outsourced in db.js;
* ---------------------------------------------------------------------------------------
* 01-30-2021 | jreyes | added express sessions; uuid for unique strings;
* | | added request to fetch user profile.
* ---------------------------------------------------------------------------------------
*/
require('dotenv').config();
const express = require("express");
const app = express();
const db = require('./db');
const bcrypt = require("bcryptjs");
const cors = require("cors");
const {v4: uuidv4} = require('uuid');
const session = require('express-session');
const pgSession = require('connect-pg-simple')(session);
app.use(express.json());
app.use(express.urlencoded());
app.use(session({
genid: (req) => {
console.log("Inside middleware, not set yet: ");
console.log(req.sessionID);
return uuidv4();
},
store: new pgSession({
pool: db,
tableName: "session"
}),
secret: process.env.ES_SECRET,
cookie:{
maxAge:36000,
httpOnly: false,
secure: false
},
resave: false,
saveUninitialized: true
}));
app.use(cors({
origin: "http://localhost:3000",
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
}));
/** Set proper headers */
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Credentials", true);
res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
res.header("Access-Control-Allow-Origin", "http://localhost:3000");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
next();
});
/**
* Test request for sessions.
*/
app.get("/", (req, res) => {
console.log('Hit detected: defualt home route');
console.log('Session detected: ' + req.sessionID);
res.json({
"hit":"!/",
"session": "SessionID: " + req.sessionID
});
});
/**
* Register a new user.
*
* Errors:
* !email: email has been taken
* !register: database malfunction
* !hashing: hashing malfunction
*
* */
app.post("/signup", async (req, res) => {
const userExistsReq = "SELECT * FROM users WHERE email = ";
const userExistsRes = await db.query(userExistsReq,[req.body.email]);
// Email already exists in the database.
if(userExistsRes.rowCount > 0){
res.status(200);
res.json({"error":"!email"});
}
else{
try {
// Hash password.
const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(req.body.password, salt)
const registerTemplate = "INSERT INTO users (email, password, firstname, lastname) VALUES (,,,)";
// Add user to database.
try {
const registerRes = await db.query(registerTemplate,
[
req.body.email,
hashedPassword,
req.body.firstname,
req.body.lastname
]
);
res.status(201);
res.json({"good":"register"});
// Error adding user.
} catch (err) {
res.status(500);
console.error("error: " + err);
res.json({"error":"!register"});
}
}
// Error hashing password.
catch {
res.status(500);
console.error("error: " + err)
res.json({"error":"!hashing"});
}
}
});
/**
* Login an existing user.
*
* Errors:
*
* !email: user does not exist
* !password: user entered wrong password
* !login: database malfunction.
*/
app.post("/login", async (req, res) => {
// Verify user presence in db.
const userExistsReq = "SELECT * FROM users WHERE email = ";
const userExistsRes = await db.query(userExistsReq, [req.body.email]);
// User does not exits.
if(userExistsRes.rowCount == 0){
res.status(200);
res.json({"error":"!email"});
}
else{
// Test user credentials.
try {
if(await bcrypt.compare(req.body.password, userExistsRes.rows[0].password)){
const email = userExistsRes.rows[0].email;
const firstname = userExistsRes.rows[0].firstname;
res.status(200);
res.json({"good":"login"})
}else{
res.status(200);
res.json({"error":"!password"})
}
// Error finding user.
} catch (err) {
res.status(200);
console.error("Error while running: " + err);
res.json({"error":"!login"});
}
}
});
/**
* Fetch the roster of players.
*
* !roster: database malfunction
*/
app.get("/roster", async (req, res) => {
const fetchRosterTemplate = "SELECT * FROM users";
const response = await db.query(fetchRosterTemplate);
if (response.rowCount == 0) {
res.status(200);
res.json({"error":"!roster"});
} else {
res.status(200);
res.json(response.rows);
}
});
/**
* Start server.
*/
app.set("port", 8080);
app.listen(app.get("port"), () => {
console.log(`Find the server at http://localhost:${ app.get("port") }`);
});
服务器控制台日志
这是我客户端的花名册页面发出两次请求后的控制台。我点击按钮两次,这些是记录的两件事。
jreyes@x1carbon:~/Projects/mothers-rfc/server$ node index.js
body-parser deprecated undefined extended: provide extended option index.js:29:17
Find the server at http://localhost:8080
Inside middleware, not set yet:
undefined
Hit detected: default home route
Session detected: db9af88c-0101-4bf5-82c7-f57fbe9dac1d
Inside middleware, not set yet:
undefined
Hit detected: default home route
Session detected: b1a5ffd2-c986-4932-827c-a6ce644a0b3e
设置 httpOnly 解决了我的问题。我把它设置为假,它必须是真的。我将 cookie 的安全选项设置为 false。
httpOnly: true
解决了我的问题:)
- 我在 localhost:8080 上为我的后端使用 express
- 在 localhost:3000 上为我的前端使用 React
- 没有使用代理。只是简单的 http://localhost:3000/roster 发送一个请求到 http://localhost:8080/
- 我也为 cors() 设置了所有交叉原点和 header 东西
- 我的 postgres 存储已设置,当我查询 sessions table 时,我可以在那里看到 session 数据。
- 在我的前端使用 axios 进行抓取,我也在那里配置了
credentials:true
。
我在控制台中将我的 sessionID 记录在中间件中(在设置之前),然后我在用于测试我的 session 的请求中再次记录它。在中间件中,它按预期无法识别,然后一旦我将其记录在请求中,我就会按预期获得真实的 session id。一切似乎都正常,但是当我第二次发送请求时,它发回了一个全新的 sessionID 而不是保留。
当我进一步查看时,它说它可能与未设置 cookie 有关?但我已经仔细检查了 resave
和 httpOnly
等正确配置设置,所有这些似乎也都还可以。
我做错了什么,我该如何解决?我首先在此处包含了客户端和服务器的 package.json....
PACKAGE.JSON 客户
{
"name": "client.react",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.21.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "^5.2.0",
"react-scripts": "0.9.5"
},
"devDependencies": {},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
PACKAGE.JSON 服务器
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.19.0",
"connect-pg-simple": "^6.2.1",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"express-session": "^1.17.1",
"jsonwebtoken": "^8.5.1",
"pg": "^8.5.1",
"uuid": "^8.3.2"
}
}
客户端
/**
* File: /src/pages/roster.js
* Date: 01-28-2021
* Author: jreyes
*
* Date | Author | Change
* ---------------------------------------------------------------------------------------
* 01-29-2021 | jreyes | initialization
* ---------------------------------------------------------------------------------------
*/
import React from 'react';
import Axios from 'axios';
const Roster = () => {
const [roster, setRoster] = React.useState([]);
/** Fetch the roster. */
const handleRoster = () => {
Axios.get("http://localhost:8080/", {
headers: {
"Content-Type":"application/json"
}
}, { withCredentials: true })
.then((response) => {
console.log(response.data);
})
}
return (
<React.Fragment>
<h1>Roster</h1>
<button onClick={handleRoster}>Get Roster</button>
</React.Fragment>
)
}
export default Roster;
来自 CHROME
的客户端控制台日志注意从服务器发回的两个不同的 sessionID。在同一个花名册页面上,我只需单击一次按钮,然后再单击一次。
{hit: "!/", session: "SessionID: db9af88c-0101-4bf5-82c7-f57fbe9dac1d"}
hit: "!/"
session: "SessionID: db9af88c-0101-4bf5-82c7-f57fbe9dac1d"
__proto__: Object
roster.js:25
{hit: "!/", session: "SessionID: b1a5ffd2-c986-4932-827c-a6ce644a0b3e"}
hit: "!/"
session: "SessionID: b1a5ffd2-c986-4932-827c-a6ce644a0b3e"
__proto__: Object
服务器
/**
* File: index.js
* Date: 01-20-2021
* Author: Bennm23
*
* Date | Author | Change
* ---------------------------------------------------------------------------------------
* 01-20-2021 | benm23 | initialization
* ---------------------------------------------------------------------------------------
* 01-29-2021 | jreyes | formatted code; fixed cors; added env
* | | functionality; db is outsourced in db.js;
* ---------------------------------------------------------------------------------------
* 01-30-2021 | jreyes | added express sessions; uuid for unique strings;
* | | added request to fetch user profile.
* ---------------------------------------------------------------------------------------
*/
require('dotenv').config();
const express = require("express");
const app = express();
const db = require('./db');
const bcrypt = require("bcryptjs");
const cors = require("cors");
const {v4: uuidv4} = require('uuid');
const session = require('express-session');
const pgSession = require('connect-pg-simple')(session);
app.use(express.json());
app.use(express.urlencoded());
app.use(session({
genid: (req) => {
console.log("Inside middleware, not set yet: ");
console.log(req.sessionID);
return uuidv4();
},
store: new pgSession({
pool: db,
tableName: "session"
}),
secret: process.env.ES_SECRET,
cookie:{
maxAge:36000,
httpOnly: false,
secure: false
},
resave: false,
saveUninitialized: true
}));
app.use(cors({
origin: "http://localhost:3000",
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
}));
/** Set proper headers */
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Credentials", true);
res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
res.header("Access-Control-Allow-Origin", "http://localhost:3000");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
next();
});
/**
* Test request for sessions.
*/
app.get("/", (req, res) => {
console.log('Hit detected: defualt home route');
console.log('Session detected: ' + req.sessionID);
res.json({
"hit":"!/",
"session": "SessionID: " + req.sessionID
});
});
/**
* Register a new user.
*
* Errors:
* !email: email has been taken
* !register: database malfunction
* !hashing: hashing malfunction
*
* */
app.post("/signup", async (req, res) => {
const userExistsReq = "SELECT * FROM users WHERE email = ";
const userExistsRes = await db.query(userExistsReq,[req.body.email]);
// Email already exists in the database.
if(userExistsRes.rowCount > 0){
res.status(200);
res.json({"error":"!email"});
}
else{
try {
// Hash password.
const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(req.body.password, salt)
const registerTemplate = "INSERT INTO users (email, password, firstname, lastname) VALUES (,,,)";
// Add user to database.
try {
const registerRes = await db.query(registerTemplate,
[
req.body.email,
hashedPassword,
req.body.firstname,
req.body.lastname
]
);
res.status(201);
res.json({"good":"register"});
// Error adding user.
} catch (err) {
res.status(500);
console.error("error: " + err);
res.json({"error":"!register"});
}
}
// Error hashing password.
catch {
res.status(500);
console.error("error: " + err)
res.json({"error":"!hashing"});
}
}
});
/**
* Login an existing user.
*
* Errors:
*
* !email: user does not exist
* !password: user entered wrong password
* !login: database malfunction.
*/
app.post("/login", async (req, res) => {
// Verify user presence in db.
const userExistsReq = "SELECT * FROM users WHERE email = ";
const userExistsRes = await db.query(userExistsReq, [req.body.email]);
// User does not exits.
if(userExistsRes.rowCount == 0){
res.status(200);
res.json({"error":"!email"});
}
else{
// Test user credentials.
try {
if(await bcrypt.compare(req.body.password, userExistsRes.rows[0].password)){
const email = userExistsRes.rows[0].email;
const firstname = userExistsRes.rows[0].firstname;
res.status(200);
res.json({"good":"login"})
}else{
res.status(200);
res.json({"error":"!password"})
}
// Error finding user.
} catch (err) {
res.status(200);
console.error("Error while running: " + err);
res.json({"error":"!login"});
}
}
});
/**
* Fetch the roster of players.
*
* !roster: database malfunction
*/
app.get("/roster", async (req, res) => {
const fetchRosterTemplate = "SELECT * FROM users";
const response = await db.query(fetchRosterTemplate);
if (response.rowCount == 0) {
res.status(200);
res.json({"error":"!roster"});
} else {
res.status(200);
res.json(response.rows);
}
});
/**
* Start server.
*/
app.set("port", 8080);
app.listen(app.get("port"), () => {
console.log(`Find the server at http://localhost:${ app.get("port") }`);
});
服务器控制台日志
这是我客户端的花名册页面发出两次请求后的控制台。我点击按钮两次,这些是记录的两件事。
jreyes@x1carbon:~/Projects/mothers-rfc/server$ node index.js
body-parser deprecated undefined extended: provide extended option index.js:29:17
Find the server at http://localhost:8080
Inside middleware, not set yet:
undefined
Hit detected: default home route
Session detected: db9af88c-0101-4bf5-82c7-f57fbe9dac1d
Inside middleware, not set yet:
undefined
Hit detected: default home route
Session detected: b1a5ffd2-c986-4932-827c-a6ce644a0b3e
设置 httpOnly 解决了我的问题。我把它设置为假,它必须是真的。我将 cookie 的安全选项设置为 false。
httpOnly: true
解决了我的问题:)