当使用 JWT 单击某个 link 时,如何重定向用户登录

How can I redirect a user to login when a certain link is clicked using JWT

我正在尝试做到这一点,以便当用户点击某个 Link(上传组件)时,它会将他们重定向到登录组件,但我不确定如何在 React 中完成此任务. 我被引导到另一个已回答的问题,但它对我没有帮助,因为我仍然对自己的设置需要做什么感到困惑。 我知道我需要创建自己的受保护路由(也许),但我看到其他人正在访问 useContext 而我没有任何包含上下文的文件。我在 React dom 中使用版本 6。我也在我的项目中使用 React 路由器和 redux,所以我知道我需要以某种方式访问​​状态,只是不确定如何围绕它思考,所以如果有人能提供帮助,我将不胜感激。用户正在使用 JWT 身份验证存储在本地存储中。

App.js:

import React, {useState, useEffect, useCallback} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom'; //Switch was replaced by Routes in react-router-dom v6
import './App.css';
import Navbar from './components/Navbar';
import Footer from './components/Footer';
import Home from './components/pages/Home';
import Uploads from './components/pages/Uploads';
import Account from './components/pages/Account';
import Login from './components/pages/Login';
import LogOut from './components/Logout';
import Register from './components/pages/Register';
import Profile from './components/pages/Profile';
import EditProfile from './components/pages/EditProfile';
import BoardAdmin from './components/pages/BoardAdmin';
import BoardModerator from './components/pages/BoardModerator';
import BoardUser from './components/pages/BoardUser';
import {logout} from './slices/auth';
import EventBus from './common/EventBus';

const App = () => {
    const [showModeratorBoard, setShowModeratorBoard] = useState(false);
    const [showAdminBoard, setShowAdminBoard] = useState(false);
    const {user: currentUser} = useSelector((state) => state.auth);
    const dispatch = useDispatch();

    useEffect(() => {
        if (currentUser) {
            setShowModeratorBoard(currentUser.roles.includes('ROLE_MODERATOR'));
            setShowAdminBoard(currentUser.roles.includes('ROLE_ADMIN'));
        } else {
            setShowModeratorBoard(false);
            setShowAdminBoard(false);
        }
    }, [currentUser]);

    return (
        <>
            <Router>
                <Navbar />
                <Routes>
                    <Route path='/' element={<Home />} />
                    <Route path='/create/upload' element={<Uploads />} />
                    <Route path='/my-account' element={<Account />} />
                    <Route path='/login' element={<Login />} />
                    <Route path='/logout' element={<LogOut />} />
                    <Route path='/register' element={<Register />} />
                    <Route path='/profile' element={<Profile />} />
                    <Route path='/profile/edit' element={<EditProfile />} />
                    <Route path='/user' element={<BoardUser />} />
                    <Route path='/mod' element={<BoardModerator />} />
                    <Route path='/admin' element={<BoardAdmin />} />
                </Routes>
                <Footer />
            </Router>
        </>
    );
};

export default App;

Auth.js:

import {createSlice, createAsyncThunk} from '@reduxjs/toolkit';
import {setMessage} from './messages';
import AuthService from '../services/auth.service';
const user = JSON.parse(localStorage.getItem('user'));
export const register = createAsyncThunk(
    'auth/register',
    async ({username, email, password}, thunkAPI) => {
        try {
            const response = await AuthService.register(username, email, password);
            thunkAPI.dispatch(setMessage(response.data.message));
            return response.data;
        } catch (error) {
            const message =
                (error.response &&
                    error.response.data &&
                    error.response.data.message) ||
                error.message ||
                error.toString();
            thunkAPI.dispatch(setMessage(message));
            return thunkAPI.rejectWithValue();
        }
    }
);
export const login = createAsyncThunk(
    'auth/login',
    async ({username, password}, thunkAPI) => {
        try {
            const data = await AuthService.login(username, password);
            return {user: data};
        } catch (error) {
            const message =
                (error.response &&
                    error.response.data &&
                    error.response.data.message) ||
                error.message ||
                error.toString();
            thunkAPI.dispatch(setMessage(message));
            return thunkAPI.rejectWithValue();
        }
    }
);
export const logout = createAsyncThunk('auth/logout', async () => {
    await AuthService.logout();
});
const initialState = user
    ? {isLoggedIn: true, user}
    : {isLoggedIn: false, user: null};
const authSlice = createSlice({
    name: 'auth',
    initialState,
    extraReducers: {
        [register.fulfilled]: (state, action) => {
            state.isLoggedIn = false;
        },
        [register.rejected]: (state, action) => {
            state.isLoggedIn = false;
        },
        [login.fulfilled]: (state, action) => {
            state.isLoggedIn = true;
            state.user = action.payload.user;
        },
        [login.rejected]: (state, action) => {
            state.isLoggedIn = false;
            state.user = null;
        },
        [logout.fulfilled]: (state, action) => {
            state.isLoggedIn = false;
            state.user = null;
        },
    },
});
const {reducer} = authSlice;
export default reducer;

在我的上传文件中添加 if 语句似乎使登录用户可以访问文件中的 h1 元素,但未经身份验证的用户不能。所以它在一定程度上起作用,只是不重定向回登录。

Upload.jsx:

import React from 'react';
import {Link} from 'react-router-dom';
import {useSelector} from 'react-redux';
function Uploads() {
    const {user: currentUser} = useSelector((state) => state.auth);
    if (!currentUser) {
        return <Link to='/login' />;
    }
    return (
        <>
            <h1 className='page'>UPLOADS</h1>
        </>
    );
}

export default Uploads;

呈现 Link 不强制导航,使用 Navigation 组件。

示例:

import React, { useEffect } from 'react';
import { Navigate } from 'react-router-dom';
import { useSelector } from 'react-redux';

function Uploads() {
  const { user: currentUser } = useSelector((state) => state.auth);

  if (!currentUser) {
    return <Navigate to='/login' replace />;
  }
  return (
    <>
      <h1 className='page'>UPLOADS</h1>
    </>
  );
}

export default Uploads;

通常你会保护路线。如果你想这样做:

import { Navigate, Outlet } from 'react-router-dom';
import { useSelector } from 'react-redux';

const PrivateRoutes = () => {
  const { user: currentUser } = useSelector((state) => state.auth);
  return currentUser 
    ? <Outlet />
    : <Navigate to="/login" replace />;
}

...

<Routes>
  <Route path='/' element={<Home />} />
  <Route element={<PrivateRoutes />}>
    <Route path='/create/upload' element={<Uploads />} />
    ... any other protected routes ...
  </Route>
  ... other unprotected routes ...
</Routes>