如何修复react.js主页授权在设置JWT cookie之前运行?

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

我一直在 React 中编写博客文章应用程序,现在我尝试使用 JWT 令牌(使用 Jsonwebtoken npm 库)向应用程序添加身份验证和授权。我正在使用 Node js 后端。

工作流程是这样的,注册 > 主页(这就是问题发生的地方)

问题

  • 注册页面注册用户并返回一个 json 令牌作为“jwt”,该令牌存储在 cookie 中。
  • 成功注册会将我重定向到主页,通过检查 cookie 是否设置来检查 useEffect 中的 authorization
  • 但是我主页的这部分代码,
if (!cookies.jwt) {
navigate('/signUp');
            }

似乎总是让我跳回到注册页面

  • 经过一些实验,我发现JWT cookie的设置速度比用户重定向所需的时间慢,所以当我进入主页时,cookie尚未设置。
  • 但是我可以手动返回主页并通过成功授权访问它。

这是我第一次在 Stackoverflow 上发布问题,如果我没有提供足够的信息,我很抱歉,我已经尽力了。请随时向我询问更多信息。

注册码:

import BookIcon from '@mui/icons-material/Book';
import axios from 'axios';
import { Avatar, Box, Container, CssBaseline, Typography, TextField, Grid, Link, Button, FormControlLabel, Checkbox } from '@mui/material';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { Toaster, toast } from 'sonner';
import { useState, useEffect } from 'react'
import { useNavigate } from 'react-router-dom';




function SignUp() {
    const navigate = useNavigate();
    const [signUpStatus, setsignUpStatus] = useState("");
    const handleSubmit = async (e) => {
        e.preventDefault();
        const email = e.target.email.value;
        const password = e.target.password.value;
        const signUpPostRequest = async () => {
            const result = await axios.post('http://localhost:3000/register', { email: email, password: password }, {
                withCredentials: true,
            }).then(async (data) => {
                console.log(data);
                if (data.data) { setsignUpStatus('Signed Up!'); }
                navigate('/homepage');
                console.log('hi');
            }).catch((data) => {
                const responseMsg = data;
                console.log(responseMsg);
                setsignUpStatus(responseMsg);
            });
        }
        setsignUpStatus("Signing Up...");
        signUpPostRequest();
    }
    const customTheme = createTheme({
        palette: {
            primary: {
                light: '#757ce8',
                main: '#31363F',
                dark: '#222831',
                contrastText: '#fff',
            },
            secondary: {
                light: '#EEEEEE',
                main: '#31363F',
                dark: '#222831',
                contrastText: '#000',
            },
        }
    })

    return (
        <>
            <ThemeProvider theme={customTheme}>
                <Container component="main" maxWidth='xs'>
                    <CssBaseline />
                    <Box sx={{
                        marginTop: 8,
                        display: "flex",
                        flexDirection: "column",
                        alignItems: "center",
                    }}>
                        <Avatar sx={{
                            m: 1,
                            color: "secondary.light",
                            bgcolor: "primary.dark"
                        }}>
                            <BookIcon
                            //  color="grey"
                            ></BookIcon>
                        </Avatar>
                        <Typography align="center" component="h1" variant="h5">Sign Up</Typography>

                        <Box component="form"
                            onSubmit={handleSubmit}
                            sx={{
                                mt: 1
                            }}>
                            <TextField
                                margin="normal" required id="email" label="Email" name="email" variant="standard" fullWidth></TextField>
                            <TextField
                                margin="normal" required id="password" label="Password" name="password" variant="standard" fullWidth
                            ></TextField>

                            <FormControlLabel sx={{
                                // mt: 1,
                            }}
                                control={
                                    <Checkbox label="remember"></Checkbox>
                                }
                                label="Remember me"
                            />
                            <Button type="submit"
                                sx={{ mt: 3, mb: 2 }}
                                variant="contained" fullWidth>Sign Up</Button>
                            <Grid container>
                                <Grid item xs>
                                    <Link href="./"
                                        underline="none" variant="body2">
                                        {"Already have an account? Sign In"}
                                    </Link>
                                </Grid>
                                <Grid item>
                                    {/* <Link
                                        underline="none" href="#"
                                        variant="body2">
                                        Forgot password?
                                    </Link> */}
                                </Grid>
                            </Grid>
                            <Box element="div">
                                <Typography>
                                    {signUpStatus}
                                </Typography>
                            </Box>
                        </Box>
                    </Box>
                </Container>
            </ThemeProvider>
        </>
    )
}

export default SignUp;

首页代码:

import * as React from 'react'
import { useState, useEffect } from 'react'
import axios from 'axios';
// import SimpleBackdrop from './backdropComp'
import { useNavigate } from 'react-router-dom';
// import HomePageContent from './HomePageContent'
import HomePageContent from './HomePageCotent'
import AppBarComp from './AppBarComp'
import { Adb as AdbIcon, Menu as MenuIcon, Book as BookIcon } from '@mui/icons-material';
import { Avatar, Box, Container, CssBaseline, Typography, TextField, Grid, Link, Button, FormControlLabel, Checkbox, AppBar, IconButton, Tooltip, MenuItem, Menu, Toolbar } from '@mui/material';
import { useCookies } from 'react-cookie';


var blogContent = "Be subject to all the higher powers; for there is no power but of God: now those which are have been ordained of God. Romans 13:1 And again, in reference to them he says, For he bears not the sword in vain; for he is the minister of God, the avenger for wrath to him who does evil. Romans 13:4 Now, that he spoke these words, not in regard to angelical powers, nor of invisible rulers — as some venture to expound the passage — but of those of actual human authorities, [he shows when] he says, For this cause pay tribute also: for they are God's ministers, doing service for this very thing.";
blogContent = blogContent.length > 500 ? blogContent.substring(0, 500) + "..." : blogContent

function HomePage() {
    const navigate = useNavigate();
    const [cookies, setCookie, removeCookie] = useCookies([]);
    const [posts, setPosts] = useState(false);

    useEffect(() => {
        console.log(cookies);
        const authorizaUser = async () => {
            if (!cookies.jwt) {
                navigate('/signUp');
            }
            const data = await axios.get('http://localhost:3000/', {
                withCredentials: true
            }).then(({ data }) => {
                console.log(data);
                if (!data.isExpired) {
                    console.log(data.message, data.data);
                    var posts = data.data.map((element, index) =>
                    <HomePageContent key={element._id} postTitle={element.postTitle} postContent={element.postContent} />
                    )
                    setPosts(posts);
                    return
                }
            }).catch(({ response }) => {
                console.log(response.data.message);
                if (response.data.isExpired) navigate('/');
                return
            })
        }
        authorizaUser()
    }, [cookies, removeCookie, navigate])

    // authorization();
    return (
        <>
            <AppBarComp />
            {posts}
        </>
    )
}

export default HomePage;

用于客户端注册和主页的 Node.js 控制器:

localhost:3000/register (POST) - 注册用户并将其引导至主页

async function registerController(req, res) {
    const email = req.body.email;
    const password = req.body.password;
    console.log('Email: ' + email + ' Password: ' + password);
    await userModel.create({ username: email, password: password, posts: [{ postTitle: "kjlj", postContent: "lkjlkj" }] }).then(async (data) => {
        console.log(data);
        const id = data._id;
        const token = jwt.sign({ id }, 'bar', { expiresIn: 1 * 24 * 60 * 60 });
        res.cookie("jwt", token, {
            withCredentials: true,
            httpOnly: false,
            maxAge: 1 * 24 * 60 * 60 * 1000,
        });
        await res.status(200).json({
            userId: data._id,
            message: "Registered the user!",
        })
    }).catch((err) => {
        if (err.code == 11000) {
            res.status(403).json({
                message: "Email ID should be unique"
            })
        }
        console.log(err);
        console.log("error exists");
    })
}

localhost:3000/ (GET) - 每次用户访问主页时进行授权

async function homeController(req, res, next) {
    // Finding the posts of user, while authorizing their request also
    async function getPosts({ id }) {
        await userModel.findOne({ _id: id }).then((data) => {
            // console.log(data.posts);
            res.status(200).json({
                isExpired: false,
                message: "Token is still valid",
                data: data.posts
            })
            return
        })
    }
    authorize(req, res, { operation: getPosts, test: 1 });
}

我尝试授权主页页面,它似乎将我重定向回注册页面,因为注册后设置的 cookie 比重定向到主页所需的反应时间慢。

javascript reactjs node.js typescript mern
1个回答
0
投票

我终于在我的问题评论部分从wraiford得到了解决方案。 就像他指出的那样,我通过正确使用异步技术重构了我的代码,而之前,我在 Axios post/get 请求上同时使用了await.then(),我认为这是导致这种现象的原因称为“竞争条件”,正如“wraiford”也指出的那样。所以这次我正确地使用了 trycatch 块,并且仅使用 await 语句来异步处理 Axios 请求。 这是我解决问题之前所做的不同的做法, 注册页面,handleSubmit函数

const handleSubmit = async (e) => { e.preventDefault(); const email = e.target.email.value; const password = e.target.password.value; setsignUpStatus("Signing Up..."); try { const responseData = await signUpPostRequest(email, password); console.log(responseData); console.log(cookies); setsignUpStatus('Signed Up!'); navigate('/homepage') // Navigate or perform any other action upon successful signup } catch (error) { console.error(error); setsignUpStatus("Error: " + error.message); } } const signUpPostRequest = async (email, password) => { try { const response = await axios.post('http://localhost:3000/register', { email, password }, { withCredentials: true, }); return response.data; } catch (error) { throw error.response.data || error.message; } }

首页、useEffect、authorizeUser功能

useEffect(() => { const authorizaUser = async () => { if (!cookies.jwt) { navigate('/signUp'); return } try { const { data } = await axios.get('http://localhost:3000/', { withCredentials: true }) console.log(data); if (!data.isExpired) { console.log(data.message, data.data); var posts = data.data.map((element, index) => <HomePageContent key={element._id} postTitle={element.postTitle} postContent={element.postContent} /> ) setPosts(posts); return } } catch ({ response }) { console.log(response.data.message); if (response.data.isExpired) navigate('/'); return } } authorizaUser(); }, [cookies, removeCookie, navigate])

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