在生产环境中从react到expressJs后端进行api调用时,Token未定义

问题描述 投票:0回答:1

我正在开发一个从 React 前端到 ExpressJs 后端进行 API 调用的应用程序,一切在本地开发中都运行良好,但在生产中却不起作用。

我在 Heroku 上托管后端,在 Netlify 上托管前端。

我可以登录,但无法查看我的个人资料,当我尝试查看我的个人资料时,它说令牌/cookie 未定义。

这是我的后台登录

const loginUser = async (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }

  const { email, password } = req.body;

  try {
    // Check if the user exists
    const user = await User.findOne({ email });
    if (!user) {
      return res.status(400).json({ msg: "Invalid credentials" });
    }

    // Check if the password matches
    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) {
      return res.status(400).json({ msg: "Invalid credentials" });
    }

    // Create a payload with user ID and role (if applicable)
    const payload = {
      user: {
        id: user.id,
        role: user.role,
      },
    };

    // Sign a JWT token
    jwt.sign(
      payload,
      process.env.JWT_SECRET,
      { expiresIn: "2h" }, // Token valid for 2 hours
      (err, token) => {
        if (err) throw err;

        // Set the token as an HTTP-only cookie
        res.cookie("token", token, {
          httpOnly: true,
          secure: process.env.NODE_ENV === "production", // Use secure cookies in production
          sameSite: "strict", // Prevent CSRF attacks
          maxAge: 2 * 60 * 60 * 1000, // 2 hours in milliseconds
        });

        // Respond with user data and token
        res.status(200).json({
          user: {
            id: user.id,
            email: user.email,
            first_name: user.first_name,
            last_name: user.last_name,
            role: user.role,
          },
          token: token, // Include the token in the response body
        });
      }
    );
  } catch (err) {
    console.error(err.message);
    res.status(500).json({ msg: "Server error" });
  }
};

以及路线

router.post(
  "/api/login",
  [
    check("email", "Please include a valid email").isEmail(),
    check("password", "Password is required").exists(),
  ],
  userController.loginUser
);

前端登录

import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";

const UserLoginForm = ({ setUser }) => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");
  const navigate = useNavigate();

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const response = await axios.post(
        "https://app.herokuapp.com/users/api/login",
        { email, password },
        { withCredentials: true }
      );
      setUser(response.data.user);
      console.log(response.data);
      localStorage.setItem("user", JSON.stringify(response.data.user));
      navigate("/");
    } catch (err) {
      setError(err.response?.data?.message || "An error occurred during login");
    }
  };

  return (
    <div className="min-h-screen bg-gray-100 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
      <div className="sm:mx-auto sm:w-full sm:max-w-md">
        <h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
          Sign in to your account
        </h2>
      </div>

      <div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
        <div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
          <form className="space-y-6" onSubmit={handleSubmit}>
            <div>
              <label
                htmlFor="email"
                className="block text-sm font-medium text-gray-700"
              >
                Email address
              </label>
              <div className="mt-1">
                <input
                  id="email"
                  name="email"
                  type="email"
                  autoComplete="email"
                  required
                  value={email}
                  onChange={(e) => setEmail(e.target.value)}
                  className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
                />
              </div>
            </div>

            <div>
              <label
                htmlFor="password"
                className="block text-sm font-medium text-gray-700"
              >
                Password
              </label>
              <div className="mt-1">
                <input
                  id="password"
                  name="password"
                  type="password"
                  autoComplete="current-password"
                  required
                  value={password}
                  onChange={(e) => setPassword(e.target.value)}
                  className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
                />
              </div>
            </div>

            <div>
              <button
                type="submit"
                className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
              >
                Sign in
              </button>
            </div>
          </form>

          {error && (
            <div className="mt-4 text-center text-red-600">{error}</div>
          )}
        </div>
      </div>
    </div>
  );
};

export default UserLoginForm;

中间件

const jwt = require("jsonwebtoken");
const User = require("../models/User");
const Salon = require("../models/Salon");

const authMiddleware = async (req, res, next) => {
  console.log(req.cookies, "this is cookies");
  try {
    // Get token from cookie
    const token = req.cookies.token || req.header("x-auth-token");

    // Check if no token
    if (!token) {
      req.user = null;
      req.salon = null;
      res.locals.authenticatedEntityId = null;
      return next();
    }

    // Verify token
    const decoded = jwt.verify(token, process.env.JWT_SECRET);

    if (decoded.user) {
      // If the token contains user information
      const user = await User.findById(decoded.user.id).select("-password");

      if (!user) {
        throw new Error("User not found");
      }

      req.user = user;
      req.salon = null;
      res.locals.authenticatedEntityId = user._id;
    } else if (decoded.salon) {
      // If the token contains salon information
      const salon = await Salon.findById(decoded.salon.id).select("-password");

      if (!salon) {
        throw new Error("Salon not found");
      }

      req.salon = salon;
      req.user = null;
      res.locals.authenticatedEntityId = salon._id;
    } else {
      // If the token doesn't contain user or salon information
      req.user = null;
      req.salon = null;
      res.locals.authenticatedEntityId = null;
    }

    next();
  } catch (error) {
    console.error("Auth Middleware Error:", error);
    req.user = null;
    req.salon = null;
    res.locals.authenticatedEntityId = null;
    next();
  }
};

module.exports = authMiddleware;

中间件返回未定义的令牌

当我登录时,它会在 cookie 中显示令牌,但如果我单击任何链接或刷新页面,令牌就会消失,因此我无法查看我的个人资料或执行任何需要我使用用户的操作。

请记住,它非常适合本地开发,但不适用于生产。

我的 Api 托管在 Heroku 上,前端托管在 netlify 上

reactjs node.js express
1个回答
0
投票

当您的前端和后端托管在不同的域上时(例如前端为 Netlify,后端为 Heroku),您可能会因 cookie 中的 sameSite 属性而遇到跨源请求问题。如果您设置 SameSite:“strict”,浏览器可能会阻止 Cookie 在这些域之间发送,这可能会干扰身份验证或会话持久性。

要解决此问题,您应该使用 sameSite: "none" 而不是 "strict"。但是,当使用 sameSite: "none" 时,cookie 还必须标记为安全,这意味着它只会通过 HTTPS 连接发送。

© www.soinside.com 2019 - 2024. All rights reserved.