在 Next.js 中创建用于身份验证的 HOC(高阶组件)

Create a HOC (higher order component) for authentication in Next.js

所以我在 Next.js 应用程序中创建身份验证逻辑。我在处理请求的地方创建了 /api/auth/login 页面,如果用户数据良好,我将创建一个带有 JWT 令牌的 httpOnly cookie 并将一些数据返回到前端。那部分工作正常,但我需要一些方法来保护某些页面,以便只有登录的用户才能访问它们,我在为此创建 HOC 时遇到问题。

我看到的最好的方法是使用 getInitialProps 但在 Next.js 网站上它说我不应该再使用它了,所以我考虑使用 getServerSideProps 但那没有要么不起作用,要么我可能做错了什么。

这是我的 HOC 代码: (cookie 存储在 userToken 名称下)

import React from 'react';
const jwt = require('jsonwebtoken');

const RequireAuthentication = (WrappedComponent) => {

  return WrappedComponent;
};


export async function getServerSideProps({req,res}) {
  const token = req.cookies.userToken || null;

// no token so i take user  to login page
  if (!token) {
      res.statusCode = 302;
      res.setHeader('Location', '/admin/login')
      return {props: {}}
  } else {
    // we have token so i return nothing without changing location
       return;
     }
}
export default RequireAuthentication;

如果您对如何使用 cookie 处理 Next.js 中的身份验证有任何其他想法,我将不胜感激,因为我是服务器端呈现的新手 react/auth。

您应该将您的身份验证逻辑从 getServerSideProps 分离并提取到可重用的高阶函数中。

例如,您可以使用以下函数来接受另一个函数(您的 getServerSideProps),如果 userToken 未设置,则会重定向到您的登录页面。

export function requireAuthentication(gssp) {
    return async (context) => {
        const { req, res } = context;
        const token = req.cookies.userToken;

        if (!token) {
            // Redirect to login page
            return {
                redirect: {
                    destination: '/admin/login',
                    statusCode: 302
                }
            };
        }

        return await gssp(context); // Continue on to call `getServerSideProps` logic
    }
}

然后您可以通过包装 getServerSideProps 函数在您的页面中使用它。

// pages/index.js (or some other page)

export const getServerSideProps = requireAuthentication(context => {
    // Your normal `getServerSideProps` code here
})

根据 Julio 的回答,我让它适用于 iron-session:

import { GetServerSidePropsContext } from 'next'
import { withSessionSsr } from '@/utils/index'

export const withAuth = (gssp: any) => {
    return async (context: GetServerSidePropsContext) => {
        const { req } = context
        const user = req.session.user

        if (!user) {
            return {
                redirect: {
                    destination: '/',
                    statusCode: 302,
                },
            }
        }

        return await gssp(context)
    }
}

export const withAuthSsr = (handler: any) => withSessionSsr(withAuth(handler))

然后我像这样使用它:

export const getServerSideProps = withAuthSsr((context: GetServerSidePropsContext) => {
    return {
        props: {},
    }
})

我的 withSessionSsr 函数如下所示:

import { GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next'
import { withIronSessionApiRoute, withIronSessionSsr } from 'iron-session/next'
import { IronSessionOptions } from 'iron-session'

const IRON_OPTIONS: IronSessionOptions = {
    cookieName: process.env.IRON_COOKIE_NAME,
    password: process.env.IRON_PASSWORD,
    ttl: 60 * 2,
}

function withSessionRoute(handler: NextApiHandler) {
    return withIronSessionApiRoute(handler, IRON_OPTIONS)
}

// Theses types are compatible with InferGetStaticPropsType https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops
function withSessionSsr<P extends { [key: string]: unknown } = { [key: string]: unknown }>(
    handler: (
        context: GetServerSidePropsContext
    ) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>
) {
    return withIronSessionSsr(handler, IRON_OPTIONS)
}

export { withSessionRoute, withSessionSsr }