【问题标题】:Render 404 page if the user has entered the wrong URL and is not authenticated in Next.js如果用户输入了错误的 URL 并且未在 Next.js 中进行身份验证,则呈现 404 页面
【发布时间】:2021-10-15 09:08:18
【问题描述】:

请参考以下代码:

Auth.tsx

import { createContext, useEffect, useState, useContext, FC } from 'react';

interface Props {
  // any props that come into the component
}

export const PUBLIC_ROUTES = ['/', '/admin/login'];

export const isBrowser = () => typeof window !== 'undefined';

const AuthContext = createContext({
  isAuthenticated: false,
  isLoading: false,
  user: {},
});

export const useAuth = () => useContext(AuthContext);

export const AuthProvider: FC<Props> = ({ children }) => {
  const [user, setUser] = useState({});
  const [isLoading, setLoading] = useState(true);

  useEffect(() => {
    async function loadUserFromCookies() {
      // const token = Cookies.get('token');
      // TODO: Get the token from the cookie
      const token = true;

      if (token) {
        // console.log("Got a token in the cookies, let's see if it is valid");
        // api.defaults.headers.Authorization = `Bearer ${token}`;
        // const { data: user } = await api.get('users/me');
        // if (user) setUser(user);
      }

      setLoading(false);
    }

    loadUserFromCookies();
  }, []);

  return (
    <AuthContext.Provider
      value={{ isAuthenticated: !!Object.keys(user).length, user, isLoading }}
    >
      {children}
    </AuthContext.Provider>
  );
};

export const ProtectRoute: FC<Props> = ({ children }) => {
  const { isAuthenticated, isLoading } = useAuth();

  if (isLoading) {
    return <div>Loading</div>;
  }

  if (PUBLIC_ROUTES.includes(window.location.pathname)) {
    return <>{children}</>;
  }

  // If the user is not on the browser or not authenticated
  if (!isBrowser() || !isAuthenticated) {
    window.location.replace('/login');
    return null;
  }

  return <>{children}</>;
};

_app.tsx

import React from 'react';
import Head from 'next/head';
import { AppProps } from 'next/dist/next-server/lib/router/router';
import { ThemeProvider, StyledEngineProvider } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import theme from '../utils/theme';
import { AuthProvider, ProtectRoute } from 'contexts/auth';

export default function MyApp(props: AppProps) {
  const { Component, pageProps } = props;

  React.useEffect(() => {
    // Remove the server-side injected CSS.
    const jssStyles = document.querySelector('#jss-server-side');

    if (jssStyles && jssStyles.parentElement) {
      jssStyles.parentElement.removeChild(jssStyles);
    }
  }, []);

  return (
    <React.Fragment>
      <Head>
        <title>Next App</title>
        <meta
          name="viewport"
          content="minimum-scale=1, initial-scale=1, width=device-width"
        />
      </Head>

      <StyledEngineProvider injectFirst>
        <ThemeProvider theme={theme}>
          {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
          <CssBaseline />
          <AuthProvider>
            <ProtectRoute>
              <Component {...pageProps} />
            </ProtectRoute>
          </AuthProvider>
        </ThemeProvider>
      </StyledEngineProvider>
    </React.Fragment>
  );
}

问题:

  1. 因此,根据代码,如果用户未登录,则重定向用户 到登录页面。但是,由于当前的逻辑,404 页 路由也被重定向到管理员登录页面。我怎么能抓住 将 404 状态重定向到 404 页面,然后再验证是否 用户是否登录?
  2. 我正在使用一个数组来验证路径是否是公共的。有没有 在不维护硬编码的情况下呈现公共路径的更好方法 页面路径还是使用 HOC?我的方法的问题是,如果任何开发人员更改了文件名,并且如果它与数组中的字符串不匹配,那么它将不是公共路由。如果我在其中创建一个名为 public 的文件夹和所有公共页面,我会在我的 URL 中得到一个不必要的 public/。我不想使用 HOC,因为每次创建新页面时我都必须导入它们,这并不正确,但我期待更好的方法。

谢谢。

【问题讨论】:

    标签: javascript reactjs authentication routes next.js


    【解决方案1】:
    1. 您可以使用 HOC(高阶组件)来包装以下路径:
    • 只有授权才能访问;
    • 只能在以下情况下访问 未经身份验证的用户(例如 /login -> 您不希望用户 登录后访问/login页面)

    像这样:

    login.js -> /login

    import withoutAuth from 'path/withoutAuth';
    
    function Login() {
      return (<>YOUR COMPONENT</>);
    };
    
    export default withoutAuth(Login);
    

    withAuth.js 也可以做到这一点 -> HOC(高阶组件)封装了只能通过身份验证访问的组件

    protectedPage.js -> /protectedPage

    import withAuth from 'path/withAuth';
    
    function ProtectedPage() {
      return (<>YOUR COMPONENT</>);
    };
    
    export default withAuth(ProtectedPage);
    

    您可以在本文中了解如何制作这些 HOC。

    1. 设置身份验证和withConditionalRedirect(用于制作withAuthwithoutAuth HOC的HOC):Detecting Authentication Client-Side in Next.js with an HttpOnly Cookie When Using SSR
    2. 制作withAuth & withoutAuth HOCs: Detecting a User's Authenticated State Client-Side in Next.js using an HttpOnly Cookie and Static Optimization

    【讨论】:

    • 是的,我在各种博客文章中都看到过这种方法。但是,我对这种方法的唯一问题是,每当我创建新页面时,我们都必须使用该 HOC。我宁愿将与路线相关的所有内容都放在一个地方,也不愿在每个页面上都使用 HOC。我认为您的解决方案没有任何问题,但也许我期待一些更清洁和有条理的东西。我什至不认为我的方法(公共路线阵列)更清洁。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-15
    • 2017-08-05
    • 2020-12-31
    • 1970-01-01
    • 2020-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多