You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

登录凭证匹配数据库但React登录表单无法跳转至首页问题排查

问题

开发了一个React登录表单,注册功能正常,但登录时出现异常:

  • 输入不匹配凭证时,控制台报错 Login.js:44 POST http://localhost:3001/login 401 (Unauthorized)
  • 输入匹配凭证时,401错误消失,但控制台输出自定义的"login failed"日志,无法跳转至首页

已确认能连接数据库并校验凭证,修改URL端点无效,附上前端登录表单代码和后端服务器代码。

原因分析
  1. 前端路由跳转逻辑错误:
    自定义的withRouter高阶组件传递给Login组件的是router对象(包含navigate方法),但代码中判断的是props.history是否存在——实际上props中根本没有history属性,导致即使登录成功(response.status===200),条件也永远不成立,直接进入else分支输出"login failed"。同时react-router-dom v6已废弃history.push,改用navigate方法。

  2. 后端CORS配置缺失凭证支持:
    登录使用了session和passport,需要前端请求携带cookie凭证,但默认的cors()配置不允许跨域携带凭证,可能导致session无法正常建立,影响后续登录状态验证。

解决方案

前端代码修改

方案1:直接使用useNavigate(推荐,无需自定义withRouter)

删除自定义的withRouter高阶组件,在Login组件中直接调用useNavigate:

import React from "react";
import Input from "./Input";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { useNavigate } from "react-router-dom";

const schema = yup.object({
  username: yup.string().required("Username is a required field"),
  password: yup.string().min(6, "Password must be at least 6 characters"),
});

function Login(props) {
  const navigate = useNavigate();
  const {
    handleSubmit,
    register,
    formState: { errors },
  } = useForm({
    resolver: yupResolver(schema),
  });

  const formSubmit = (data) => {
    fetch("http://localhost:3001/login", {
      method: "POST",
      body: JSON.stringify({username: data.username, password: data.password}),
      headers: { "Content-Type": "application/json" },
      credentials: "include",
    })
    .then(async (response) => {
      if (response.ok) {
        const result = await response.json();
        console.log(result.message);
        navigate("/");
      } else {
        const error = await response.json();
        console.log("login failed:", error.message);
      }
    })
    .catch((error) => {
      console.error("Error:", error);
    });
  };

  return (
    <div className="sign-up">
      <h1>Log in</h1>
      <p>Lorem ipsum dolor, sit amet consectetur</p>
      <form onSubmit={handleSubmit(formSubmit)}>
        <Input
          id="username"
          label="Username"
          type="text"
          placeholder="Enter Username"
          register={{ ...register("username") }}
          errorMessage={errors.username?.message}
        />
    
        <Input
          id="password"
          label="Password"
          type="password"
          placeholder="Enter Password"
          register={{ ...register("password") }}
          errorMessage={errors.password?.message}
        />

        <button>Log in</button>
      </form>
      <button className="button-link" onClick={() => props.onFormSwitch("signup")}>
        Don't have an account? Register here.
      </button>
    </div>
  );
}

export default Login;

方案2:修复自定义withRouter的props传递

如果坚持使用withRouter,需将navigate直接作为props传递:

// 修改withRouter函数
function withRouter(Component) {
  function ComponentWithRouterProp(props) {
    let location = useLocation();
    let navigate = useNavigate();
    let params = useParams();
    return (
      <Component
        {...props}
        navigate={navigate}
        router={{ location, navigate, params }}
      />
    );
  }

  return ComponentWithRouterProp;
}

// 修改formSubmit中的跳转逻辑
const formSubmit = (data) => {
  fetch("http://localhost:3001/login", {
    method: "POST",
    body: JSON.stringify({username: data.username, password: data.password}),
    headers: { "Content-Type": "application/json" },
    credentials: "include",
  })
  .then(async (response) => {
    if (response.ok) {
      props.navigate("/");
    } else {
      console.log("login failed");
    }
  })
  .catch((error) => {
    console.error("Error:", error);
  });
};

后端代码修改

修复CORS配置,支持跨域凭证

替换默认的cors配置,指定允许的前端origin并开启凭证支持:

// 替换原来的app.use(cors());
app.use(cors({
  origin: "http://localhost:3000", // 你的前端项目运行端口,通常为3000
  credentials: true
}));

额外建议:密码加密存储

当前明文存储密码存在安全风险,建议使用bcrypt哈希加密:

  1. 安装依赖:npm install bcrypt
  2. 注册时加密密码:
const bcrypt = require("bcrypt");
const saltRounds = 10;

// 在注册路由中
bcrypt.hash(password, saltRounds, (err, hash) => {
  if (err) throw err;
  const newUser = new Signup({ username, password: hash });
  newUser.save();
});
  1. 登录时验证哈希:
// 修改passport的local策略
passport.use(
  'local',
  new LocalStrategy(function (username, password, done) {
    Signup.findOne({ username: username }, function (err, user) {
      if (err) {
        return done(err);
      }
      if (!user) {
        return done(null, false);
      }
      bcrypt.compare(password, user.password, (err, result) => {
        if (err) return done(err);
        result ? done(null, user) : done(null, false);
      });
    });
  })
);

内容的提问来源于stack exchange,提问作者user19102549

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 15:55:18