【问题标题】:Create a HOC (higher order component) for authentication in Next.js在 Next.js 中创建一个 HOC(高阶组件)进行身份验证
【发布时间】:2025-11-30 04:00:01
【问题描述】:

所以我在我的 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 的新手。

【问题讨论】:

    标签: node.js cookies next.js server-side-rendering higher-order-components


    【解决方案1】:

    您应该将身份验证逻辑从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
    })
    

    【讨论】:

    • 好的,我的 req.headers 中什至没有 cookie 对象,我只是开始一个新线程,因为我不确定这是否与您的答案有关,但非常感谢您的帮助!
    • @deadcoder0904 将gssp 输入为requireAuthentication(gssp: GetServerSideProps) 对我有用。关于您遇到的错误,您可能需要为此创建一个新问题,因为我认为它与此问题无关。
    • @deadcoder0904 您可能想为您遇到的打字问题创建一个新问题。
    • @juliomalves 我会的,但这不是什么大问题。现在使用any :)
    • 有道理-谢谢!!
    【解决方案2】:

    根据 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 }
    

    【讨论】: