【问题标题】:express-session setting new session every time, and does not persist after creating the sessionexpress-session 每次都设置新的会话,创建会话后不持久
【发布时间】:2021-05-04 06:30:32
【问题描述】:
  • 我在 localhost:8080 上使用 express 作为后端
  • 在 localhost:3000 上为我的前端使用 react
  • 没有使用代理。只需简单的 http://localhost:3000/roster 发送请求到 http://localhost:8080/
  • 我也为 cors() 设置了所有的交叉原点和标头内容
  • 我的 postgres 存储已设置,当我查询会话表时,我可以在其中看到会话数据。
  • 在我的前端使用 axios 进行 fetch,我也在那里配置了 credentials:true

我正在控制台中将我的 sessionID 记录在中间件中(在设置之前),然后我在用于测试会话的请求中再次记录它。在中间件中,它如预期的那样无法识别,然后一旦我将它记录在请求中,我就会按预期得到一个真实的会话 ID。一切似乎都正常,但是当我第二次发送请求时,它会发回一个全新的 sessionID 而不是持久化。

当我进一步查看时,它说这可能与未设置 cookie 有关?但是我已经到处寻找正确配置设置的方法,例如 resavehttpOnly,而且所有这些似乎都还可以。

我做错了什么,我该如何解决这个问题?我首先在这里为客户端和服务器包含了我的 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 = $1";
    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 ($1,$2,$3,$4)";

            // 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 = $1";
    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

【问题讨论】:

    标签: postgresql express cookies axios express-session


    【解决方案1】:

    设置 httpOnly 解决了我的问题。我把它设置为假,它必须是真的。我将 cookie 的安全选项设置为 false。

    httpOnly: true
    

    解决了我的问题:)

    【讨论】:

      猜你喜欢
      • 2019-11-04
      • 2021-04-21
      • 1970-01-01
      • 2017-12-07
      • 2021-06-15
      • 1970-01-01
      • 2014-04-25
      • 2020-11-16
      • 2019-01-06
      相关资源
      最近更新 更多