我如何在 NextJs 中创建私有和公共路由?

时间:2021-02-27 23:57:27

标签: reactjs authentication next.js

我正在研究 Nextjs,我来自 React,很多似乎都一样,但我迷失了身份验证和私有路由。 我在互联网上寻找了很多代码,但要么它们坏了,要么对我没有意义,要么它们没有正确解释我的疑虑。我的场景基本上是:

  • 我有一个申请
  • 此应用程序具有公共和私有路由
  • 用户需要登录才能访问私人路线
  • 私人路线具有相同的导航栏

我的问题是:

  • 如何使用 ReactContext 创建私有路由。
  • 如何在页面之间共享相同的 NavBar(无需在每个屏幕上放置 NavBar 组件)
  • 如何使用我自己的代码正确验证用户,最好使用 ReactContext
  • 如何在认证后重置路由(如果用户点击浏览器上的后退按钮,则无法返回登录屏幕)
  • 如何正确保存 JWT 令牌,以便将用户的会话保存更长时间,以便他不需要再次登录

到目前为止,我的代码可以正常工作,但我确信它很糟糕,而且完全有缺陷。

我有以下文件:

  • _app.js(Nextjs 项目的根目录)
  • index.js(登录页面)
  • privateRoute.js(验证用户是否登录并允许或不允许其访问的文件)
  • userContext.js(保存用户信息以供其他组件和页面访问的文件)
  • NavContext.js(检查是否有人登录以呈现 NavBar 的文件)

_app.js

function MyApp({ Component, pageProps }) {
  return (
    <UserContextFunc>
      <NavContext>
        <Component {...pageProps} />
      </NavContext>
    </UserContextFunc>
  );
}

export default MyApp;

index.js

export default function App() {
  const [loading, setLoading] = React.useState(false);
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");

  const { setState } = React.useContext(UserContext);

  const router = useRouter();

  const login = () => {
    setLoading(true);
    fetch("/api/auth/login", {
      method: "POST",
      body: JSON.stringify({
        email,
        password,
      }),
      headers: {
        "Content-Type": "application/json",
      },
    })
      .then((res) => {
        setLoading(false);
        if (res.status === 200) {
          res.json().then(({ token, roles }) => {
            setState({ roles, token });
            window.localStorage.setItem(
              "rou",
              btoa(unescape(encodeURIComponent(JSON.stringify(roles))))
            );
            window.localStorage.setItem("token", token);
            router.replace("/app/adm/home");
          });
        } 
      })
      .catch((err) => {

      });
  };

  return (
    <>
      <ReactNotification />

      <label htmlFor="exampleInputEmail1" className="form-label">
        Email
      </label>
      <input
        type="text"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        className="form-control"
      />

      <label htmlFor="exampleInputPassword1" className="form-label">
        Senha
      </label>
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        className="form-control"
      />

      <button
        onClick={() => login()}
        type="button"
        className="btn btn-primary btn-block"
      >
        {loading ? (
          <div
            className="spinner-border text-light spinner-border-sm"
            role="status"
          >
            <span className="visually-hidden"></span>
          </div>
        ) : (
          "Login"
        )}
      </button>
    </>
  );
}

privateRoute.js

const indexPage = "/";

const withAuth = (Component) => {
  const Auth = (props) => {
    const { setState } = React.useContext(UserContext);

    const router = useRouter();

    React.useEffect(() => {
      const token = window.localStorage.getItem("token");

      var roles = window.localStorage.getItem("rou");

      if (roles) {
        roles = decodeURIComponent(escape(atob(roles)));
      }

      if (!token || !roles || token == "undefined") {
        window.localStorage.removeItem("token");
        window.localStorage.removeItem("rou");
        return router.replace(indexPage);
      } else {
        setState({ roles, token });
      }
    }, []);

    return <Component {...props} />;
  };

  if (Component.getInitialProps) {
    Auth.getInitialProps = Component.getInitialProps;
  }

  return Auth;
};

export default withAuth;

userContext.js

export const UserContext = React.createContext();

export const UserContextFunc = ({ children }) => {
    const [state, dispatch] = React.useReducer(
        (prevState, action) => {
            switch (action.type) {
                case 'SET':
                    return {
                        ...prevState,
                        ...action.newState,
                    };
            }
        },
        {
            roles: []
        }
    );

    const setState = newState => {
        dispatch({ type: 'SET', newState });
    }

    const getState = async () => {
        return state
    }

    return (
        <UserContext.Provider value={{ getState, setState, state }}>
            {children}
        </UserContext.Provider>
    );
}

NavContext.js

function NavContext(props) {
  const { state } = React.useContext(UserContext);

  return (
    <>
      {state.roles && state.token && <NavBar />}
      {props.children}
    </>
  );
}

export default NavContext;

在私人文件中,我以这种方式导出

import withPrivateRoute from "../../../utils/privateRoute";
...
export default withPrivateRoute(Dashboard);

我希望我能很好地解释它,我知道它很多,但我没有找到任何解释如何在 Nextjs 中创建私有路由或如何在不使用 Next 文档中的身份验证模板的情况下正确身份验证的内容.

这段代码有效,但正如我所说,它似乎完全错误。我也接受小费。

0 个答案:

没有答案