如何使用JWT保护服务器端的路由不受RequireAuth组件的影响?



我读到授权应该由服务器端完成,而不仅仅是客户端。但我看不清这是什么样子。以下是我目前编写的代码。

App.js

这表明在渲染页面之前,它要经过RequireAuth组件。

import { useContext} from 'react'
import './style.scss'
import Register from './pages/Register/Register'
import TeacherLogin from './pages/Login/TeacherLogin/TeacherLogin'
import StudentLogin from './pages/Login/StudentLogin/StudentLogin'
import ForgotPassword from './pages/ForgotPassword/ForgotPassword'
import PageNotFound from './pages/PageNotFound/PageNotFound'
import Home from './pages/Home/Home'
import AccountType from './pages/AccountType/AccountType'
import Users from './pages/Students/Students'
import SOTW from './pages/SOTW/SOTW'
import Landing from './pages/Landing/Landing'
import Add from './pages/Add/Add'
import Test from './pages/Test/Test'
import Edit from './pages/Edit/Edit'
import ClassStats from './pages/ClassStats/ClassStats'
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
import { DarkModeContext } from './Hooks/context/darkModeContext'
import Profile from './StudentPages/Profile/Profile'
import RequireAuth from './components/RequireAuth/RequireAuth'
function App() {
const {darkMode} = useContext(DarkModeContext)
return (
<div className={`theme-${darkMode ? "dark" : "light"}`}>
<BrowserRouter>
<Routes>
<Route path='/' element={<Landing />} />
<Route path="/register" element={<Register/>}/>
<Route path="forgot-password" element={<ForgotPassword/>} />
<Route path="test" element={<Test/>} />
<Route path="/auth">
<Route index element={<AccountType/>} />
<Route path ="teacher" element={<TeacherLogin/>} />
<Route path="student" element={<StudentLogin/>} />
</Route>
<Route path="/auth/teacher" element={<RequireAuth allowedRoles={['teacher']}/>}>
<Route path='dashboard' element={<Home/>} />
<Route path="SOTW" element={<SOTW/>}/>
<Route path="users">
<Route index element={<Users />} />
<Route path="add" element={<Add title='Add Student' button='Add' />} />
<Route path="edit" element={<Edit title='Edit Student' button='Update' />} />
</Route>
</Route>
<Route path="auth/student" element={<RequireAuth allowedRoles={['student', 'teacher']}/>}>
<Route path='profile/:id' element={<Profile/>} />
</Route>
<Route path="*" element={<PageNotFound/>}/>
</Routes> 
</BrowserRouter>
</div>
);
}
export default App;

RequireAuth

这是为用户检查本地存储的组件。这是有效的,但并不安全。

import { Navigate, useLocation, Outlet } from 'react-router-dom';
import { AuthContext } from '../../Hooks/context/AuthContext';
import { useContext, useEffect, useState } from 'react';
import axios from 'axios';
const RequireAuth = ({ allowedRoles }) => {
const { testUser, setTestUser, user } = useContext(AuthContext);
const [statusAuth, setStatusAuth] = useState(false);
const location = useLocation();
console.log(user)
useEffect(() => {
const fetch = async () => {
const res = await axios.get('/api/auth/protected')
console.log(res) // THIS IS WHERE I AM STUCK. WHAT DO I DO HERE?
}
fetch()
},[])

return allowedRoles.includes(user?.role) ? (
<Outlet />
) : user ? (
<Navigate to="/unauthorised" state={{ from: location }} replace />
) : (
<Navigate to="/" state={{ from: location }} replace />
);
};
export default RequireAuth;

使用Node.js的后台代码

这是验证令牌代码,用于验证令牌并检查用户是否是"教师"。然而,这是不灵活的。例如,如果一个页面只能被一个不同的用户看到,这个用户是"学生",那么它就不能工作,因为它会检查他们是否是"教师"角色。

import jwt from 'jsonwebtoken'
import { createError } from './error.js'
export const verifyToken = (req, res, next) => {
const token = req.headers.cookie.split('=')[1]
if (!token) {
return next(createError(401, "You are not authenticated!"));
}
jwt.verify(token, process.env.JWT, (err, user) => {
if (err) return next(createError(403, "Token is not valid!"));
req.user = user;
next();
});
};
export const verifyAdmin = (req, res, next) => {
verifyToken(req, res, () => {
if(req.user.role === 'teacher') {
next()
} else {
return next(createError(403, 'You are not authorised'))
}
})
}

路线

import express from 'express'
import {register, login, refreshToken, logout, protectedRoute} from '../Controllers/auth.js'
import { verifyToken, verifyAdmin } from '../verifyToken.js';
const router = express.Router();
//PROTECTED ROUTE
router.get("/protected", verifyAdmin, protectedRoute)
export default router
控制器

import mongoose from 'mongoose'
import Student from '../Models/student.js'
import bcrypt from 'bcrypt'
import { createError } from '../error.js'
import jwt from 'jsonwebtoken'

export const protectedRoute = async (req, res, next) => {
res.status(200).send('Success')
}

我已经到了验证令牌的阶段。我是否对每个向后端请求的页面都这样做,以确定用户是否可以实际查看该页面?

如果是这样,我如何使我的require验证组件灵活地根据用户是:student, teacher, admin或其他重定向?

我觉得我在这里作弊,但一个简单的安装jwt-decode解决了它。

它的实现包含在RequireAuth组件中,如下所示:

import { Navigate, useLocation, Outlet } from 'react-router-dom';
import { AuthContext } from '../../Hooks/context/AuthContext';
import { useContext, useEffect, useState } from 'react';
import axios from 'axios';
import jwtDecode from 'jwt-decode'
const RequireAuth = ({ allowedRoles }) => {
const { user } = useContext(AuthContext);
const [statusAuth, setStatusAuth] = useState(false);
const location = useLocation();
const decoded = jwtDecode(user)
return allowedRoles.includes(decoded?.role) ? (
<Outlet />
) : user ? (
<Navigate to="/unauthorised" state={{ from: location }} replace />
) : (
<Navigate to="/" state={{ from: location }} replace />
);
};
export default RequireAuth;

但是,仍然没有后端验证。有必要吗?如果用户在登录时经过验证,并且JWT签名并交付,那么是否可以信任他们访问其他路由?

任何指导都会很好。

最新更新