在按下登录按钮并进行身份验证后,我希望应用程序将我定向到所需的页面.但当我按下按钮时,它不会重定向,或者即使我输入了错误的密码,它也会转到所需的页面并返回.因为它还没有被鉴定过.我找不到正确的路.

通过身份验证后,我想转到包含资源列表的页面.此页面的路径为"/".

我在此分享几段代码,以便让您有所了解. 应用程序:

const Layout = ({ children }) => {
  return (
    <div className="link">
      <MiniDrawer>
        <Outlet />
      </MiniDrawer>
    </div>
  );
};

const router = createBrowserRouter([
  {
    path: "/login",
    element: <Login />,
  },

  {
    path: "/",
    element: (
      <AdminGuard>
        <Layout />
      </AdminGuard>
    ),
    children: [
      {
        path: "/",
        element: <Resources />,
      },
      {
        path: "/create",
        element: <CreateResource />,
      },
      {
        path: "/edit/:id",
        element: <EditResource />,
      },
    ],
  },
]);

function App({ Component }) {
  return (
    <div className="App">
      <RouterProvider router={router} />
    </div>
  );
}

export default App;

-看这个.

import React from "react";
import { Route, Navigate, Outlet } from "react-router-dom";
import { AuthService } from "../services/auth.services";

const AdminGuard = ({ children }) => {
  if (AuthService.isAuthenticated()) {
    return children;
  } else {
    return <Navigate to="/login" />;
  }
};

export default AdminGuard;

登录页面:

import { useDispatch } from "react-redux";
import LoginView from "./view";
import { AuthService } from "../../services/auth.services";

function Login() {
  const dispatch = useDispatch();
  function onSubmit({ username, password }) {
    dispatch(AuthService.userLogin({ username, password }));
  }

  return (
    <>
      <LoginView onSubmit={onSubmit} />
    </>
  );
}

export default Login;

身份验证服务:

import { createAsyncThunk } from "@reduxjs/toolkit";
import { axiosApiInstance } from "./axios.services";
import { TokenService } from "./token.service";

export const AuthService = {};
AuthService.userLogin = createAsyncThunk(
  "userSlice/userLogin",
  async ({ username, password }, { rejectWithValue }) => {
    try {
      const response = await axiosApiInstance.post(
        "https://localhost:7105/api/Connect",
        { username, password },
        {
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            username: typeof username === "string" ? username.toString() : "",
            password: typeof password === "string" ? password.toString() : "",
          }),
        }
      );

      const tokenResponse = { accessToken: response?.data };
      TokenService.setToken(tokenResponse);

      return tokenResponse;
    } catch (error) {
      return rejectWithValue(error);
    }
  }
);

AuthService.isAuthenticated = () => {
  return localStorage.getItem("access_token") ? true : false;
};
AuthService.logout = () => {
  TokenService.clearToken();
  window.location.href = "/login";
};

令牌服务:

export const TokenService = {};

TokenService.setToken = ({ accessToken }) => {
  localStorage.setItem("access_token", accessToken);
};

TokenService.clearToken = () => {
  localStorage.removeItem("access_token");
};

身份验证切片:

import { useNavigate } from "react-router-dom";
import { AuthService } from "../../services/auth.services";
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";

const initialState = {
  data: {},
  isLoading: false,
  hasError: false,
};

const authSlice = createSlice({
  name: "authSlice",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(AuthService.userLogin.pending, (state) => {
        state.isLoading = true;
        state.hasError = false;
      })
      .addCase(AuthService.userLogin.fulfilled, (state, { payload }) => {
        state.pending = false;
        state.data = payload;
      })
      .addCase(AuthService.userLogin.rejected, (state) => {
        state.isLoading = false;
        state.hasError = true;
      });
  },
});

export const selectAuthState = (state) => state.authSlice.data;
export default authSlice.reducer;

推荐答案

我没有看到任何在用户身份验证后重定向用户的内容.如果我对这个问题的理解是正确的,那么您需要更新代码,以便在他们成功通过身份验证后处理到受保护的路由的重定向.

对于这一点,我建议做一些改变.

将身份验证判断更新为有状态,而不是依赖于从本地存储读取.对本地存储的更新不会触发Reaction组件重新呈现,因此AdminGuard中的身份验证判断可能会过时.因为state.authSlice.data状态是令牌值,所以UI应该引用该值,或者具体地说是state.authSlice.data.accessToken.

import React from "react";
import { Navigate, Outlet, useLocation } from "react-router-dom";
import { useSelector } from 'react-redux';
import { selectAuthState } from "../slices/auth.slice";

const AdminGuard = () => {
  // Capture the current location for auth redirect
  const location = useLocation();

  // Access authentication state
  const { accessToken } = useSelector(selectAuthState);

  // Wait until any initial/pending auth checks complete
  if (accessToken === undefined) {
    return null; // or loading indicator/spinner/etc
  }

  // Return Outlet for nested routes if authenticated
  // Return redirect with current location in state
  return accessToken
    ? <Outlet />
    : <Navigate to="/login" redirect state={{ from: location }} />;
};

export default AdminGuard;

身份验证服务

将注销功能更新为也是一个thunk.这是因为当用户注销时,您可以添加减速器 case 来处理重置/清除state.data值.

import { createAsyncThunk } from "@reduxjs/toolkit";
import { axiosApiInstance } from "./axios.services";
import { TokenService } from "./token.service";

export const AuthService = {};

AuthService.user登录 = createAsyncThunk(
  "userSlice/user登录",
  async ({ username, password }, { rejectWithValue }) => {
    try {
      const { data } = await axiosApiInstance.post(
        "https://localhost:7105/api/Connect",
        { username, password },
        {
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            username: typeof username === "string" ? username.toString() : "",
            password: typeof password === "string" ? password.toString() : "",
          }),
        }
      );

      const tokenResponse = { accessToken: data };
      TokenService.setToken(tokenResponse);

      return tokenResponse;
    } catch (error) {
      return rejectWithValue(error);
    }
  }
);

AuthService.userLogout = createAsyncThunk(
  "userSlice/user登录",
  () => {
    TokenService.clearToken();
  }
);
const authSlice = createSlice({
  name: "authSlice",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(AuthService.user登录.pending, (state) => {
        state.isLoading = true;
        state.hasError = false;
      })
      .addCase(AuthService.user登录.fulfilled, (state, action) => {
        state.pending = false;
        state.data = action.payload;
      })
      .addCase(AuthService.user登录.rejected, (state) => {
        state.isLoading = false;
        state.hasError = true;
      })
      .addCase(AuthService.userLogout.pending, (state) => {
        state.isLoading = true;
        state.hasError = false;
      })
      .addCase(AuthService.userLogout.fulfilled, (state) => {
        state.pending = false;
        state.data = {}; // <-- reset on logout
      })
      .addCase(AuthService.userLogout.rejected, (state) => {
        state.isLoading = false;
        state.hasError = true;
      });
  },
});

APP

Update APP to render the AdminGuard as a layout route component.

const router = createBrowserRouter([
  {
    path: "/login",
    element: <登录 />,
  },
  {
    element: <Layout />,
    children: [
      {
        element: <AdminGuard />,
        children: [
          {
            path: "/",
            element: <Resources />,
          },
          {
            path: "/create",
            element: <CreateResource />,
          },
          {
            path: "/edit/:id",
            element: <EditResource />,
          },
        ],
      },
    ],
  },
]);

function APP({ Component }) {
  return (
    <div className="APP">
      <RouterProvider router={router} />
    </div>
  );
}

登录

Update the 登录 component's submit handler to wait for the login action to resolve and then issue an imperative redirect back to the route path the user was originally attempting to access.

import { useLocation, useNavigate } from 'react-router-dom';
import { useDispatch } from "react-redux";
import 登录View from "./view";
import { AuthService } from "../../services/auth.services";

function 登录() {
  const dispatch = useDispatch();
  const { state } = useLocation();
  const navigate = useNavigate();

  onSubmit = async ({ username, password }) => {
    try {
      // Wait for login action to complete, e.g. resolve
      await dispatch(AuthService.user登录({ username, password })).unwrap();

      // Redirect to original target, or safe non-protected route
      const { from } = state || { from: /* safe fallback that isn't a protected route */ };
      navigate(from, { replace: true });
    } catch(error) {
      // handle errors?
    }
  }

  return (
    <>
      <登录View onSubmit={onSubmit} />
    </>
  );
}

export default 登录;

同样,现在的"注销"功能是一样的,调度AuthService.userLogout动作后重定向.

const dispatch = useDispatch();
const navigate = useNavigate();

const logout = async () => {
  try {
    await dispatch(AuthService.userLogout()).unwrap();
    navigate("/login", { replace: true });
  } catch(error) {
    // handle errors?
  }
};

Reactjs相关问答推荐

构建next.js项目时状态不变,但在dev服务器中

如何将图像(在代码中称为自己)定位在蓝色圈内的点上?我正在使用material UI和Reaction

为什么登录错误和密码在创建Reaction-Hook-Form后立即被删除?

如何处理具有REACTION-REDUX状态的Nextjs路由警卫

在Reaction中的第一次装载时,Use Effect返回空数组

GitHub页面配置

Gitlab CI和VITE环境变量出现问题

生产部署:控制台中 Firebase 无效 api 密钥错误

react 本机屏幕转换

Mui DataGrid 在第二页和前一页上显示项目时出现问题

在Vite React上获取Amplify的环境变量

Zod 验证模式根据另一个数组字段使字段成为必需字段

我正在try 将一组加载程序函数发送到我的路由,它抛出一个错误 Handler is not a funtion in a reactJs application

ReactJS 动态禁用按钮

AWS 全栈应用程序部署教程构建失败

如何避免在 react useEffect 上滚动时出现小的延迟?

如何在react 日历中自定义带有圆形边框的日期项?

列表应该有一个唯一的关键props

如何实现两个 SVG 形状相互叠加的 XOR 操作?

错误未捕获的错误:[App] 不是 组件. 的所有子组件必须是