You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 如何统一检查用户登录状态及处理受保护路由?

统一处理Next.js的用户登录校验与受保护路由重定向

方案一:封装公共的getServerSideProps函数

把重复的校验逻辑抽成可复用的函数,分两种场景使用:需要登录才能访问的页面(受保护路由),以及不需要强制登录但要获取用户信息的页面。

1. 封装基础用户校验函数

新建utils/auth.js文件,把获取token、用户信息的逻辑抽出来:

// utils/auth.js
export async function getAuthenticatedUser(context) {
  const token = await getToken(context);
  if (!token) {
    return { user: null, token: null };
  }

  try {
    const profile = await getProfile(token);
    if (profile?.data?.user) {
      return { user: profile.data.user, token };
    }
  } catch (err) {
    console.error('获取用户信息失败:', err);
  }

  return { user: null, token: null };
}

2. 封装受保护路由专用校验函数

针对必须登录才能进入的页面,写个强制登录的函数,未登录直接跳转到登录页:

// utils/auth.js
export async function requireAuth(context) {
  const { user, token } = await getAuthenticatedUser(context);
  
  if (!user) {
    return {
      redirect: {
        destination: '/login',
        permanent: false,
      },
    };
  }

  return {
    props: { user, token },
  };
}

3. 封装非强制登录的校验函数

对于首页这类不需要登录也能访问,但要显示用户状态的页面,用这个函数:

// utils/auth.js
export async function getAuthProps(context) {
  const { user, token } = await getAuthenticatedUser(context);
  return {
    props: { user, token },
  };
}

使用示例

  • 受保护页面(比如/dashboard):
// pages/dashboard.js
import { requireAuth } from '../utils/auth';

export async function getServerSideProps(context) {
  return requireAuth(context);
}

export default function Dashboard({ user }) {
  return <div>欢迎,{user.name}</div>;
}
  • 非强制登录页面(比如首页/):
// pages/index.js
import { getAuthProps } from '../utils/auth';

export async function getServerSideProps(context) {
  return getAuthProps(context);
}

export default function Home({ user }) {
  return user ? <div>欢迎回来,{user.name}</div> : <div>请登录</div>;
}

方案二:用自定义_app.js全局处理(Page Router)

如果所有页面都需要统一获取用户信息,可以在_app.js里通过getInitialProps全局处理,这样每个页面的props会自动带上用户信息,还能统一判断受保护路由。

// pages/_app.js
import { getAuthenticatedUser } from '../utils/auth';

function MyApp({ Component, pageProps, user, token }) {
  return <Component {...pageProps} user={user} token={token} />;
}

MyApp.getInitialProps = async ({ Component, ctx }) => {
  const { user, token } = await getAuthenticatedUser(ctx);
  
  // 可以把受保护路由列表单独放到配置文件,方便后续维护
  const isProtectedRoute = ['/dashboard', '/profile'].includes(ctx.pathname);
  
  if (isProtectedRoute && !user) {
    if (typeof window !== 'undefined') {
      window.location.href = '/login';
    } else {
      ctx.res.writeHead(302, { Location: '/login' });
      ctx.res.end();
    }
    return {};
  }

  const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};

  return { pageProps, user, token };
};

export default MyApp;

注意:使用getInitialProps会关闭Next.js的静态优化,所有页面都会变为服务端渲染。如果部分页面需要静态生成,建议优先用方案一。

额外提示

  • 把受保护路由列表单独放到配置文件,比如config/routes.js,新增路由时直接修改配置即可:
// config/routes.js
export const PROTECTED_ROUTES = ['/dashboard', '/profile', '/settings'];
  • 记得处理token过期的情况:如果getProfile调用失败,直接清除无效cookie再跳转到登录页。
  • 如果使用Next.js 13+的App Router(App目录),用middleware.js做路由守卫更高效:
// middleware.js
import { NextResponse } from 'next/server';
import { getToken } from './utils/auth';

export async function middleware(request) {
  const path = request.nextUrl.pathname;
  const isProtectedRoute = ['/dashboard', '/profile'].includes(path);
  
  const token = await getToken(request); // 需适配App Router的token获取方式
  const user = token ? await getProfile(token) : null;

  if (isProtectedRoute && !user) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*'], // 指定需要拦截的路由
};

内容的提问来源于stack exchange,提问作者Ubeydullah Yılmaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 20:20:29