javascript - 登录表单和 protected 路由无法正常工作;登录成功后 protected 路由打不开

标签 javascript node.js reactjs authentication react-router-dom

所以我有一个登录表单,当我提交它时,它会将用户和密码发送到服务器,没有任何问题。服务器验证它们,并且它们被批准。

但是当服务器将 token 发送给客户端时,路由似乎仍然受到保护。它会被重定向大约 1/4 秒,然后返回到 /admin 路由。我希望它重定向到 /tickets 路由并保留在 /tickets 路由中。

我知道现在身份验证非常容易受到攻击,但我只是想暂时使用它来了解更多有关 protected 路由和身份验证系统的信息。这是代码:

protected .jsx

import React, { useState } from "react";
import { Outlet, Navigate } from "react-router-dom";

//Create a protected route, that can only be accessed with admin credentials
//For the prototype this will do

function Protected() {
  const token = localStorage.getItem('token');
  console.log(localStorage.getItem('token'));
  //the /admin route is the route to the login form.
  return token ? <Outlet /> : <Navigate to='/admin' />;
}

export default Protected;

service.jsx:

import React from "react";
import Home from "../Home";
import TicketingSystem from "./TicketingSystem";
import Login from "./Login";
import Protected from "./Protected";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";

//Creates protected routes, that will only allow authorized users to access with authorized credentials.
//At this point it will be hard coded, securely into the server

function Service(props) {
    
  return (
    <Router>
      <Routes>
        <Route path='/admin' element={<Login />} />
        <Route path='/' element={<Home />} />
        <Route element={<Protected />}>
          <Route path='/tickets' element={<TicketingSystem />} />
        </Route>
      </Routes>
    </Router>
  );
}
  
export default Service;

登录.jsx

import React, { useState } from "react";
import { Grid, Typography, TextField, Button } from "@mui/material";
import "./media/login.css";

function Login(props) {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const handleSubmit = async (event) => {
    event.preventDefault();

    const res = await fetch ('/login-request', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, password })
    });
    if (res.ok) {
      const { token } = await res.json();
      localStorage.setItem('token', token);
      props.history.push('/tickets')
    }
  }

  return (
    <React.Fragment>
      <Grid container justifyContent='center' alignItems='center'>
        <div id='login-block'>
          <Typography align='center'>
            <h1 id='login-title'>Admin Login</h1>
            <form onSubmit={handleSubmit}>
              <Grid item xs={12}>
                <TextField
                  id='username'
                  name='username'
                  variant='standard'
                  label='username'
                  size='small'
                />
                <br />
              </Grid>
              <Grid item xs={12}>
                <br />
                <TextField
                  id='password'
                  type='password'
                  name='password'
                  variant='standard'
                  label='password'
                  size='small'
                />
                <br />
              </Grid>
              <Grid item xs={12}>
                <br />
                <Button id='submit-login-request' variant='contained' type='submit'>
                  Login
                </Button>
              </Grid>
            </form>
            <p id='l1'>The unauthorized use of this route, is illegal.</p>
          </Typography>
        </div>
      </Grid>
    </React.Fragment>
  );
}

export default Login;

服务器.js

app.post('/login-request', (req, res, next) => {
  /*  You need to update the token in the front end to true to access tickets,
      But this is insecure, make sure the password is hashed and sent securely
      make sure users cant find vulnerabilities in the login form.
  '
  */

  console.log('recieved login request', req.body.username, req.body.password);
  const { username, password } = req.body;
  if (username === "admin" && password === "password") {
    const token = jwt.sign({ username }, 'test');
    res.cookie('jwt', token, { httpOnly: true });

    // Redirect the user to the /tickets route
    res.status(302).setHeader('Location', '/tickets').end();
  } else {
    res.status(401).json({message: "invalid credentials"});
  }
});

是否有办法做到一旦服务器发送响应并获得批准,它就会重定向并停留在 "/tickets" 路由上?

经过一番审查,我注意到 localStorage 项目没有被创建。我相信这与此有关,但我不知道它们没有被创建的原因或原因。

最佳答案

问题

您正在设置 cookie,但没有将 token 作为 JSON 响应的一部分发送到客户端。因此,如果没有抛出错误,这个 const {token} = wait res.json() 将会返回 undefined 。并且尝试使用 JavaScript 发出的请求的 header 进行重定向是行不通的。

解决方案

如果您想将 token 存储在本地存储中,解决方案是更改后端,以便以 JSON 格式发送 token ,如下所示。这应该适用于您的 React 代码的当前状态:

app.post("/login-request", (req, res, next) => {
  const { username, password } = req.body;
  if (username === "admin" && password === "password") {
    const token = jwt.sign({ username }, "test");
    res.status(200).json({ token: token });
  } else {
    return res.status(401).json({ message: "invalid credentials" });
  }
});

超越

但假设您想使用 cookie。在这种情况下,需要进行多项更改。在后端,像您已经做的那样设置 cookie,并发送 200 成功消息而不是重定向。

在 React 部分,您可以使用全局状态,例如 Context API ,存储有关登录用户的信息,您在Login页面中设置这些信息,并能够在Protected中访问。因为浏览器上的 JavaScript 无法访问 httpOnly cookie。

关于javascript - 登录表单和 protected 路由无法正常工作;登录成功后 protected 路由打不开,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76138366/

相关文章:

javascript - Node.js 实用程序模块结构

javascript - 获取 jQuery 对象/引用

mysql - Sequelize Js - 如果不存在则创建新数据库

node.js - 现在通过 brew 使用 npm 安装 Node 的方式是什么

javascript - 音频文件无法在reactjs中播放

javascript - 在 Canvas 上绘制六边形,测试鼠标单击事件与六边形

javascript - React - 你可以变异修改 this.props.children 来动态(以编程方式)添加子组件

javascript - Node.js 与 Twitter API/bot 的问题

javascript - 使用样式化组件实现图像延迟加载

node.js - 如何从外部导入默认导出的webpack入口文件?