如何使用后端Node.js Express-Sessions对React前端进行身份验证?

4

我正在摆脱使用Firestore Auth的方式,以了解如何制作自己的身份验证系统。

我的目标:

  1. 创建一个React Web应用程序,让用户注册和登录。
  2. 经过身份验证的用户可以在RestAPI上操作,该API是NodeJS连接到Postgres。

我看过很多教程。其中大部分都谈到了express-session作为解决方案。然而,当我尝试时,这些会话cookie只在我的浏览器位于express uri上时才提供。

问题是...

在前端,当我通过fetch发送CRUD操作到我的express api时,我的react-app无法获取到那个cookie。

我所做的是处理与后端服务器身份验证的常规方式吗?我该如何修复这个问题?

EXPRESS:


require("dotenv").config();
const express = require("express");
const db = require("./db");
var session = require("express-session");
var pgSession = require("connect-pg-simple")(session);

const app = express(); //create an instance;
// :id url param.
app.use(
    session({
      store: new pgSession({ pool: db.pool }),
      secret: "secret",
      resave: false,
      saveUninitialized: false,
      cookie: { maxAge: 303 * 24 * 60 * 60 * 1000 }, //30days
    })
  );

app.use(function (req, res, next) {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader(
    "Access-Control-Allow-Methods",
    "GET, POST, OPTIONS, PUT, PATCH, DELETE"
  );
  res.setHeader(
    "Access-Control-Allow-Headers",
    "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers,X-Access-Token,XKey,Authorization"
  );
  next();
});

app.use(express.json());

app.post("/addUser", async (req, res) => {
  try {
    const results = await db.query(
      "INSERT INTO USERS (email, password) values ($1, $2) returning *",
      [req.body.email, req.body.password]
    );
    console.log(results);
    req.session.isAuth = true;
    res.status(201).json({
      status: "success",
      data: {
        user: results.rows,
      },
    });

  } catch (err) {
    console.log(err);
  }
});

ReactJS


import React, { useEffect, useState } from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  Redirect,
} from "react-router-dom";
const App = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [authenticated, setAuthenticated] = useState(false);
  const onSubmit = (e) => {
    e.preventDefault();
    fetch("http://localhost:3006/addUser", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email: email, password: password }),
    })
      .then(function (response) {
        return response.json();
      })
      .then((response) => {
        console.log(response);
        setAuthenticated(true);
      })
      .catch((err) => {
        console.log(err);
      });
  };

  return (
    <Router>
      <Switch>
        <router path="/" exact>
          <div>
            <form onSubmit={(e) => onSubmit(e)}>
              <label>email</label>
              <input
                name="email"
                type="email"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
              ></input>
              <label>password</label>
              <input
                name="password"
                type="password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
              ></input>
              <input type="submit" value="submit"></input>
            </form>
          </div>
          {authenticated ? <Redirect to="/auth" /> : null}
        </router>
        <Route path="/auth" exact>
          Authenticated
        </Route>
      </Switch>
    </Router>
  );
};

export default App;

在React中-提交表单后,在devtools中看不到cookie。


请问您能否分享一下您的服务器代码和API URL示例? - WebEXP0528
是的,我已经更新了它。请看一下。谢谢。 - cozycoder
1个回答

4
  1. 在服务器上
  • 请启用 CORS(安装CORS库)。
app.use(
  cors({
    origin: true,
    credentials: true,
    optionsSuccessStatus: 200
}))
  • 添加会话配置。
app.use(
  session({
    key: 'sid',
    secret: Secret_String,
    resave: false,
    saveUninitialized: false,
    cookie: {
      path: '/',
      sameSite: 'none',
      httpOnly: true,
      secure: false,
      maxAge: 1000000000, // ten seconds, for testing
    },
    store: store, // postgres session store
  }),
);
  1. 在客户端,调用API时必须使用withcredentials选项。
  • 从Chrome 85.0版本开始,Chrome禁用了不同来源的set-cookie。所以,set-cookie将无法正常工作。您需要在Chrome中禁用安全选项。 请转到chrome://flags -> Cookies without SameSite must be secure -> 禁用
  1. 在您的代码中
fetch("http://localhost:3006/addUser", {
      method: "POST",
      credentials: 'include',
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email: email, password: password }),
})

手动提供的CORS头部不够吗? - cozycoder
您可以在浏览器开发工具->网络中检查set-cookie选项。 - WebEXP0528
重要的是在Chrome设置中禁用安全选项。 - WebEXP0528
非常感谢,这个可以用。我很可能需要了解一下CORS。同时,在之前的公共API(如jsonplaceholder)中,我从未在不使用“withcredential”时出现过获取数据的问题。所以我不确定是什么问题。如果您能回答一下,在这种设置下,我是否可以安全地进行生产部署,还是更适合本地开发的补丁工作?标准方法是什么?非常感谢。 - cozycoder
使用with sameSite: 'none'以及关闭Chrome标志,对我来说,整个方法似乎从一开始就不安全,无法在实际世界中发挥作用。 - cozycoder
Chrome浏览器禁用了85.0版本的Set-Cookie脚本以提高安全性。 - WebEXP0528

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接