我一直在 React 中编写博客文章应用程序,现在我尝试使用 JWT 令牌(使用 Jsonwebtoken npm 库)向应用程序添加身份验证和授权。我正在使用 Node js 后端。
工作流程是这样的,注册 > 主页(这就是问题发生的地方)
if (!cookies.jwt) {
navigate('/signUp');
}
似乎总是让我跳回到注册页面
这是我第一次在 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 比重定向到主页所需的反应时间慢。
我终于在我的问题评论部分从wraiford得到了解决方案。 就像他指出的那样,我通过正确使用异步技术重构了我的代码,而之前,我在 Axios post/get 请求上同时使用了await和.then(),我认为这是导致这种现象的原因称为“竞争条件”,正如“wraiford”也指出的那样。所以这次我正确地使用了 try 和 catch 块,并且仅使用 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(() => {
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])