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

dgtucam1  于 2022-12-18  发布在  Node.js
关注(0)|答案(2)|浏览(106)

我读到过授权应该由服务器端完成,而不仅仅是客户端。但是我看不清这是什么样子。下面是我目前为止的代码。
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;

要求授权
这是一个检查用户本地存储的组件。它可以工作,但是不安全。

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 auth组件灵活地根据用户是否为以下用户进行重定向:学生,老师,行政人员还是其他人

icomxhvb

icomxhvb1#

我觉得我在这里作弊,但一个简单的安装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签名并交付,那么他们是否可以被信任访问其他路由?
任何指导都很好。

2lpgd968

2lpgd9682#

这里有一个替代答案,我希望得到你的意见。
在这里,我通过POST方法将AllowedRoles传递到后端。
在后端,我解码JWT并获取用户的角色,然后检查它是否存在于授权角色中。
如果是,我发送一条成功消息。
在前端,如果res成功,那么我设置状态:允许(真)和正在检查(假)。
这看起来像我想要的那样工作,但我不确定这是最好的做法。任何意见都将非常感谢。

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 location = useLocation();
  const [allowed, setAllowed] = useState(Boolean);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetch = async () => {

      const res = ''

      if (allowedRoles.includes('student')) {
        console.log('student page')
        res = await axios.get('/api/student/protected')
      }
      if (allowedRoles.includes('teacher')) {
        console.log('teacher page')
        res = await axios.get('/api/teacher/protected')
      }
      if (allowedRoles.includes('admin')) {
        console.log('admin page')
        res = await axios.get('/api/admin/protected')
      }
      
      if (res.status === 200) {
        setAllowed(res.data);
        setLoading(false);
        return;
      } else {
        setAllowed(res.data);
        setLoading(true);
      }
    };
    fetch();
  },[location]);

  return loading ? (
    <p>Checking....</p>
  ) : !user ? (
    <Navigate to="/" state={{ from: location }} replace />
  ) : allowed ? (
    <Outlet />
  ) : (
    <Navigate to="/" state={{ from: location }} replace />
  );
};

export default RequireAuth;

后端代码

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(403, "You are not authorised"));
    }
    jwt.verify(token, process.env.JWT, (err, user) => {
        if (err) return next(createError(403, "Token is not valid!"));
        req.user = user;
        next();
      });
  };

export const verifyTeacher = (req, res, next) => {
    verifyToken(req, res, () => {
        if(req.user.role === 'teacher') {
            next()
        } else {
            return next(createError(403, 'You are not authorised'))
        }
    })
}

相关问题