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

解决NextJS路由重定向前短暂加载未授权页面的问题

解决Next.js中/app/路由未授权页面短暂加载的问题

你的问题根源在于授权校验是在客户端组件挂载后(useEffect中)才执行,这就导致未授权页面会先渲染出来,之后才触发重定向。要解决这个问题,需要把授权校验提前到路由加载之前完成,Next.js提供了两种更合适的方案:

方案一:使用Middleware(推荐,Next.js 12+)

Middleware会在所有路由请求到达页面之前运行,不管是SSG、SSR还是CSR页面都能生效,是最彻底的解决方式。

  1. 在项目根目录创建middleware.ts文件:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // 匹配所有/app/开头的路由
  const isAppRoute = request.nextUrl.pathname.startsWith('/app/')
  // 排除登录页本身
  const isLoginRoute = request.nextUrl.pathname === '/login/'

  if (isAppRoute && !isLoginRoute) {
    // 从请求Cookie中获取会话令牌(适配后端Cookie存储会话的逻辑)
    const sessionToken = request.cookies.get('session_token')?.value

    if (!sessionToken) {
      // 无会话令牌,直接重定向到登录页,可保留原请求地址作为回调
      return NextResponse.redirect(new URL('/login/?redirect=' + request.nextUrl.pathname, request.url))
    }

    // 可选:发起请求到后端校验会话有效性
    // const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/auth/session/`, {
    //   headers: { Cookie: `session_token=${sessionToken}` }
    // })
    // const data = await res.json()
    // if (!data.authenticated) {
    //   return NextResponse.redirect(new URL('/login/', request.url))
    // }
  }

  // 校验通过,继续路由
  return NextResponse.next()
}

// 指定Middleware生效的路由范围
export const config = {
  matcher: ['/app/:path*', '/login/']
}
  1. 注意事项:
    • Middleware运行在Edge Runtime环境,不能使用Node.js特定API,要用fetch代替axios
    • 优先从Cookie读取会话,避免重复发起请求,提升性能
    • 可保留原请求地址作为redirect参数,登录成功后跳转回原页面

方案二:使用getServerSideProps(针对Pages Router)

如果你的项目用Pages Router,可以在每个需要授权的/app/页面中添加getServerSideProps,在服务器端提前校验:

比如在/app/profile.js中:

import axios from 'axios'
import { getUserState } from '../context/AppSession'

export async function getServerSideProps(context) {
  const { req, res } = context

  try {
    // 携带请求Cookie发起会话校验
    const sessionRes = await axios.get('/api/auth/session/', {
      headers: { Cookie: req.headers.cookie }
    })
    const { authenticated } = sessionRes.data

    if (!authenticated) {
      // 未授权,直接重定向
      res.writeHead(302, { Location: '/login/' })
      res.end()
      return { props: {} }
    }

    // 校验通过,将用户数据传递给页面组件
    return { props: { user: sessionRes.data } }
  } catch (err) {
    // 请求失败,默认重定向到登录页
    res.writeHead(302, { Location: '/login/' })
    res.end()
    return { props: {} }
  }
}

export default function Profile() {
  const { user } = getUserState()
  return <div>用户Profile:{user.user_email}</div>
}

修正原客户端SessionContext代码(辅助优化)

如果还需要保留客户端的会话状态管理,可以优化原代码的依赖数组和初始化逻辑,避免不必要的请求:

import React, { createContext, useContext, useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import axios from 'axios'

export const SessionContext = createContext(null);

const AppSession = ({ children, initialUser }) => {
    const router = useRouter()
    // 初始化时明确authenticated状态为undefined
    const [user, setUser] = useState(initialUser || { user_id: '', user_email: '', user_avatar: '', authenticated: undefined})

    useEffect(()=> {
        const isAppRoute = router.pathname.includes("/app/")
        // 仅在/app/路由且未完成校验时发起请求
        if (isAppRoute && user.authenticated === undefined){
            axios.get('/api/auth/session/')
            .then(res => {
                const data = res.data;
                setUser(data)
                if (!data.authenticated){
                    router.push('/login/')
                }
            })
            .catch(err => {
                console.log(err)
                router.push('/login/')
            });
        }
    }, [router.pathname, user.authenticated]) // 添加路由路径和authenticated作为依赖

    return (
        <SessionContext.Provider value={{user, setUser}}>
            {children}
        </SessionContext.Provider>
    )
}

export const getUserState = () => {
    const { user } = useContext(SessionContext)
    return user;
}

export const updateUserState = () => {
    const { setUser } = useContext(SessionContext)
    return (user) => setUser(user);
}

export default AppSession;

内容的提问来源于stack exchange,提问作者cphill

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 17:45:31