我正在开发一个 Web 应用程序,使用 React 和 Vite 作为前端,Express 作为后端,MongoDB 作为数据库。我有几个受保护的路由,例如 /employees 和 /clients。这些路由要求用户登录并通过 JWT 令牌进行身份验证。
当我使用网页上的链接导航到这些路线时,如果我登录了,一切都会正常。但是,如果我尝试通过在地址栏中输入 URL 直接访问这些路线,则会收到以下错误:
{"error":"Access denied: No token provided"}
即使我登录了,也会发生这种情况,并且令牌存储在 localStorage 中。我的登录逻辑工作正常,并且在通过链接导航时,令牌会在请求标头中发送。
auth.js
const express = require("express");
const router = express.Router();
const User = require("../models/User");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const JWT_SECRET = process.env.JWT_SECRET;
// Login route
router.post("/login", async (req, res) => {
const { username, password } = req.body;
try {
const user = await User.findOne({ username });
if (!user) return res.status(400).send({ error: "Invalid credentials" });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).send({ error: "Invalid credentials" });
const token = jwt.sign({ id: user._id }, JWT_SECRET, { expiresIn: "7d" });
res.send({ token });
} catch (err) {
res.status(500).send({ error: "Internal server error" });
}
});
module.exports = router;
authMiddleware.js
const jwt = require("jsonwebtoken");
const JWT_SECRET = process.env.JWT_SECRET;
const authMiddleware = (req, res, next) => {
let token;
const authHeader = req.header("Authorization");
if (authHeader && authHeader.startsWith("Bearer ")) {
token = authHeader.replace("Bearer ", "");
}
if (!token) {
console.log("No token provided");
return res.status(401).send({ error: "Access denied: No token provided" });
}
try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
console.log("Invalid token:", err.message);
res.status(401).send({ error: "Invalid token" });
}
};
module.exports = authMiddleware;
App.jsx:
import { useEffect } from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import axios from 'axios';
import PrivateRoute from './components/PrivateRoute';
import EmployeesPage from './components/EmployeesPage';
import ClientsPage from './components/ClientsPage';
import LoginPage from './components/LoginPage';
const App = () => {
useEffect(() => {
const token = localStorage.getItem('token');
if (token) {
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
}
}, []);
return (
<Router>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/employees" element={<PrivateRoute element={<EmployeesPage />} />} />
<Route path="/clients" element={<PrivateRoute element={<ClientsPage />} />} />
</Routes>
</Router>
);
};
export default App;
PrivateRoute.jsx:
import { Navigate } from 'react-router-dom';
const PrivateRoute = ({ element, redirectTo = '/login' }) => {
const token = localStorage.getItem('token');
if (!token) {
return <Navigate to={redirectTo} />;
}
return element;
};
export default PrivateRoute;
当直接通过地址栏访问 URL 时,应用程序应识别来自 localStorage 的令牌,将其发送到标头中,并允许访问受保护的路由。
为什么我在通过地址栏直接访问路由时会收到“访问被拒绝:未提供令牌”错误,即使我已登录?在这些情况下如何确保令牌正确发送?
任何帮助或指导将不胜感激!
同意@C3roe的评论。
为了实现所需的功能,您可能需要使用 cookie 来代替存储对象或 localStorage。
Cookie 是环境权威的一种形式。服务器设置域属性来指定 cookie 将发送到的主机。当域属性与源服务器匹配时,用户代理将在发出 HTTP 请求时包含 cookie。这些 HTTP 请求可以用于嵌入内容,例如来自其他站点的图像和视频、获取请求以及顶级导航。
但是,存储对象或本地存储不能包含在顶级导航中,这是您现在遇到的困难。