我做了这个项目专门用来练习认证和安全。但是看在上帝的份上,我不能让这个东西工作。我正在使用Node.js mongoDB后端和react前端。到目前为止,我已经设法从服务器发送了一个Jwt令牌,并将其存储在浏览器的本地存储中。但是现在我坚持将这个令牌发送到后端进行验证,并将用户导航到“/”如果他/她在登录之前尝试访问'/messages'路由,则会打开“/messages”页面。下面是代码。我使用axios来处理http请求,使用react-router-dom来处理路由。
App.jsx
import { Routes, Route } from 'react-router-dom'
import Login from './pages/Login'
import Register from './pages/Register'
import Messages from './pages/Messages'
function App() {
return (
<Routes>
<Route path='/messages' element={<Messages />}/>
<Route path='/register' element={<Register />} />
<Route index element={<Login />} />
</Routes>
)
}
export default App
Messages.jsx
import React from 'react'
import axios from 'axios'
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
const Messages = () => {
const navigate = useNavigate();
const [message, setMessage] = useState("");
const [messages, setMessages] = useState([]);
const [emptyErrorMessage, setemptyErrorMessage] = useState(false);
const postMessage = async e => {
e.preventDefault();
if (!message) setemptyErrorMessage(true);
else {
await axios.post('http://localhost:5000/messages', {message});
window.location.reload(false);
}
}
const getMessages = async () => {
try {
const token = localStorage.getItem('token');
const res = await axios.get('http://localhost:5000/messages', {
headers : {
'Authorization': token
}
});
setMessages(res.data);
} catch (err) {
console.log(err);
if (err.response.status == 401) {
navigate('/');
}
}
}
useEffect(() => {
getMessages();
}, []);
return (
<div>
<form>
<label>Message:</label><br />
<input type="text" onChange={e => setMessage(e.target.value)} name='message' autoComplete='off'/>
{emptyErrorMessage ? <h5>This Field is required</h5> : null}
<button type='submit' onClick={postMessage}>Submit</button>
</form>
{messages && messages.length > 0 ? messages.map(singleMessage => {
return <h4 key={singleMessage._id}>{singleMessage.message}</h4>
}) : <h1>No messages Yet</h1>}
</div>
)
}
export default Messages
Login.jsx
import React, { useState } from 'react'
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const navigate = useNavigate();
const postLoginData = async () => {
const res = await axios.post('http://localhost:5000/auth/login', {
email,
password
});
if (res.data.status == 200) {
const token = res.data.token;
localStorage.setItem('token', token);
axios.defaults.headers.common['Authorization'] = `${token}`;
navigate('/messages')
} else window.location.reload(false);
}
return (
<>
<div className="login-form">
<form>
<label>Email:</label>
<input type="email" onChange={e => setEmail(e.target.value)} name='email' />
<label>Password:</label>
<input type="password" onChange={e => setPassword(e.target.value)} name='password' />
</form>
<button onClick={postLoginData}>Login</button>
<br />
<a href="/register">Or register</a>
</div>
</>
)
}
export default Login
这就是前端。这里是后端控制器。
userRoutes.js:
import express from 'express';
import { loginUser, registerUser } from '../controllers/userController.js';
const router = express.Router();
router.post('/login', loginUser)
router.post('/register', registerUser)
export default router;
messageRoutes.js:
import express from "express";
import { postMessage, getMessage } from "../controllers/messageControllers.js";
import verifyToken from '../middleware/verifyToken.js';
const router = express.Router();
router.post('/', postMessage);
router.get('/', verifyToken, getMessage);
export default router;
verifyToken.js:
import jwt from 'jsonwebtoken';
const verifyToken = (req, res, next) => {
const authHeader = req.headers.Authorization;
if (!authHeader) return res.status(401).json({ status: 401 });
try {
const token = authHeader.split(" ")[1];
const decoded = jwt.verify(token, `${process.env.JWT_SECRET}`)
console.log(decoded);
next()
} catch (err) {
console.log(err);
}
}
export default verifyToken;
messagesController.js
import Message from '../models/messageSchema.js'
export const postMessage = async (req, res) => {
const {message} = req.body;
const newMessage = new Message ({
message: message
});
await newMessage.save();
res
.status(200)
.json({ status: 200 });
}
export const getMessage = async (req, res) => {
const messages = await Message.find({}).exec();
res.send(messages);
}
userController.js
import User from "../models/userSchema.js";
import bcrypt from 'bcrypt';
import generateToken from '../middleware/generateToken.js'
export const loginUser = async (req, res) => {
const {email, password} = req.body;
try {
if (!email || !password) throw Error('All fields must be filled')
const potentialUser = await User.findOne({ email });
if (!potentialUser) throw Error("Invalid Login Credentials");
const match = await bcrypt.compare(password, potentialUser.password)
if (!match) throw Error("Invalid Login Credentials");
const token = generateToken(potentialUser._id);
res
.status(200)
.json({
email,
token,
status: 200
})
} catch (err) {
res
.status(400)
.json({ err: err.message })
}
}
export const registerUser = async (req, res) => {
const {email, password} = req.body;
try {
if (!email || !password) throw Error('All fields must be filled')
const userExists = await User.findOne({ email });
if (userExists) {
throw Error('User Already Exists');
}
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
const user = new User ({
email,
password: hashedPassword
})
const token = generateToken(user._id);
await user.save();
res
.status(200)
.json({ status: 200, token });
} catch(err) {
res
.status(400)
.json({ message: err.message })
}
}
关于路线的一些背景:路由的端点在主服务器文件中定义,我没有提供,因为我相信错误不是来自那里。访问/login或/register。完整路径是/auth/login,注册也是如此。在前端,“/”是登录路由,“/register”是注册路由。这两个路由都是公共的。“/messages”路由应该受到保护。其实不是。
预期行为:如果用户使用正确的用户名和密码登录,则会生成一个令牌并存储在浏览器的本地存储中,用户将被导航到“/messages”。但如果有人试图在未登录的情况下访问“/messages”,则用户应被重定向到登录页面。
实际行为:使用我提供的代码,无论用户名和密码是否匹配,我都会被重定向到登录页面。这很明显,因为在这一点上我已经对代码做了很多改动。另外,当我将令牌作为授权头发送并尝试在后端控制台记录它时,结果发现没有这样的授权头。
注意:提供的代码可能不完全有意义。请考虑到Messages.jsx和VerifyToken函数中处理数据的方式可能有很多不规则之处。另外,如何在axios中使用授权头来处理请求以及如何验证令牌也会很棒。
我认为问题可能存在于Messages.jsx文件和Verify Token函数中,尽管它也可能是用户控制器之一。
1条答案
按热度按时间e4yzc0pl1#
我没有足够的评分来发表评论,但是看起来您的
verifyToken
函数立即希望Authorization头中包含两个单词,中间用空格隔开,第二个单词是实际的标记:const token = authHeader.split(" ")[1];
,但看起来您只在axios
请求中发送令牌本身。按照惯例,您通常会有Authorization: Bearer xxx
,其中xxx
是您的令牌。我认为在axios
请求中的令牌之前添加Bearer
应该可以解决此问题。