即使在Render上部署的React Nodejs应用程序中登录后,Cookie也不会存储在浏览器中

9wbgstp7  于 2023-06-29  发布在  Node.js
关注(0)|答案(1)|浏览(112)

我已经通过以下YouTube教程构建了一个全栈MERN(React + Node.js + MongoDB)应用程序-https://www.youtube.com/watch?v=FcxjCPeicvU
项目官方GitHub repo-https://github.com/codinginflow/MERN-course
在确保前端和后端应用程序在我的本地机器上正常工作后,我决定在线部署它们。为了部署前端和后端,我使用了Render
我先把后端部署到Render,当我尝试把本地前端应用和部署的后端应用连接起来时,效果很好。我可以毫无问题地执行注册/登录等操作。
然后我也在Render上部署了我的前端应用程序(https://notes-app-xjr0.onrender.com)。当我点击上面的URL时,它毫无问题地出现在应用程序主页上。有趣的部分来了。
每当我登录/注册时,后端应该存储会话,因此应该在前端存储cookie以保持用户登录。紧接着,应用程序对notes API(https://notes-api-ttsn.onrender.com/api/notes)进行GET调用,以获取与存储在cookie中的用户凭据相关联的笔记。但在我的例子中,在成功登录(POST调用https://notes-api-ttsn.onrender.com/api/users/login)后,我再也看不到cookie了,因此notes API失败,出现了401 - User not authenticated错误(下面的屏幕截图)。

(BACKEND)app.ts中的Express-session和CORS配置

import express from "express"
import session from "express-session";
import cors from "cors";

const app = express();

app.use(
  session({
    secret: env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: {
      secure: true,
      httpOnly: false,
      sameSite: "none",
      maxAge: 60 * 60 * 1000,
    },
    rolling: true,
    store: MongoStore.create({
      mongoUrl: env.MONGO_CONNECTION_STRING,
    }),
  })
);
app.use(cors({ origin: "*", credentials: true }));

(BACKEND)controllers/users.ts中的登录控制器功能

import { RequestHandler } from "express";
import createHttpError from "http-errors";
import bcrypt from "bcrypt";
import UserModel from "../models/User";

interface LoginBody {
  username?: string;
  password?: string;
}

export const login: RequestHandler<unknown, unknown, LoginBody, unknown> = async (req, res, next) => {
  const { username, password } = req.body;

  try {
    if (!username || !password) {
      throw createHttpError(400, "Missing required fields");
    }

    const user = await UserModel.findOne({ username: username })
      .select("+password +email")
      .exec();

    if (!user) {
      throw createHttpError(401, "Invalid username or password!");
    }

    const isPasswordValid = await bcrypt.compare(password, user.password);

    if (!isPasswordValid) {
      throw createHttpError(401, "Invalid username or password!");
    }

    req.session.userId = user._id;

    res.status(201).json(user);
  } catch (error) {
    next(error);
  }
};

(BACKEND)获取controllers/notes.ts中的Notes控制器函数

import { RequestHandler } from "express";
import createHttpError from "http-errors";
import NoteModel from "../models/Note";

/* GET ALL NOTES FOR THE LOGGED IN USER */
export const getNotes: RequestHandler = async (req, res, next) => {
  const authenticatedUserId = req.session.userId;

  try {
    const notes = await NoteModel.find({ userId: authenticatedUserId }).exec();
    res.status(200).json(notes);
  } catch (error) {
    next(error);
  }
};

(FRONTEND)network/fetchData.ts中的取数功能

import { ConflictError, UnauthorizedError } from "../errors/http_errors";

export const fetchData = async (input: RequestInfo, init?: RequestInit) => {
  const apiEndpoint = env.BACKEND_URL + input;

  const response = await fetch(apiEndpoint, init);
  if (response.ok) {
    return response;
  } else {
    const errorBody = await response.json();
    const { error: errorMessage } = errorBody;

    if (response.status === 401) {
      throw new UnauthorizedError(errorMessage);
    } else if (response.status === 409) {
      throw new ConflictError(errorMessage);
    } else {
      throw Error(
        "Request failed with status : " +
          response.status +
          " message: " +
          errorMessage
      );
    }
  }
};

(FRONTEND)network/user-api.ts中的登录功能

import { User } from "../models/user";
import { fetchData } from "./fetchData";

export type LoginCredentials = {
  username: string;
  password: string;
};

export const login = async (credentials: LoginCredentials): Promise<User> => {
  const response = await fetchData("/api/users/login", {
    method: "POST",
    headers: {
      "content-type": "application/json",
    },
    body: JSON.stringify(credentials),
  });

  return response.json();
};

network/note-api.ts中的(FRONTEND)便笺取数功能

import { Note } from "../models/note";
import { fetchData } from "./fetchData";

/* GET ALL NOTES FOR THE LOGGED IN USER */
export const fetchNotes = async (): Promise<Note[]> => {
  const response = await fetchData("/api/notes", { method: "GET" });

  return response.json();
};
r8xiu3jd

r8xiu3jd1#

在使用render部署时,我也遇到了同样的问题。
从这个resource。Render使用此子域“www.example.com”部署您的后端服务器onrender.com。它是一个列出的公共后缀,因此cookie不能在不同的子域之间共享-如果您使用自己的子域可能会解决这个问题。

相关问题