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

Next.js目录结构问题致中间件认证失效,仪表盘闪显

问题解决:Next.js双角色仪表盘未认证闪显+重定向失效

核心问题根源

  1. 客户端ProtectedRoutes依赖useEffect执行重定向,组件会先渲染内容再跳转,必然出现闪显
  2. 目录内middleware.ts路由匹配逻辑错误(比如staff中间件引用了client的路由配置),且Cookie校验名称不统一(组件查token,中间件查staff/client)
  3. 目录级中间件的生效范围未正确配置,导致拦截逻辑失效

分步解决方案

1. 修复并统一中间件配置(关键)

将staff和client的认证逻辑合并到根目录app/middleware.ts,在请求到达页面之前完成拦截,彻底避免闪显:

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

// 统一路由与Cookie规则
const ROUTE_RULES = {
  staff: {
    protectedPaths: ['/staff', '/staff/:path*'],
    loginPath: '/auth/staff',
    cookieKey: 'staff'
  },
  client: {
    protectedPaths: ['/client', '/client/:path*'],
    loginPath: '/auth/client',
    cookieKey: 'client'
  }
};

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // 校验Staff路由权限
  if (ROUTE_RULES.staff.protectedPaths.some(path => pathname.startsWith(path))) {
    const staffCookie = request.cookies.get(ROUTE_RULES.staff.cookieKey)?.value;
    let isAuthValid = false;

    if (staffCookie) {
      try {
        const userData = JSON.parse(staffCookie);
        isAuthValid = Date.now() <= userData.expiredAt;
      } catch (e) {
        isAuthValid = false;
      }
    }

    if (!isAuthValid) {
      const response = NextResponse.redirect(new URL(ROUTE_RULES.staff.loginPath, request.url));
      response.cookies.delete(ROUTE_RULES.staff.cookieKey);
      return response;
    }
  }

  // 校验Client路由权限
  if (ROUTE_RULES.client.protectedPaths.some(path => pathname.startsWith(path))) {
    const clientCookie = request.cookies.get(ROUTE_RULES.client.cookieKey)?.value;
    let isAuthValid = false;

    if (clientCookie) {
      try {
        const userData = JSON.parse(clientCookie);
        isAuthValid = Date.now() <= userData.expiredAt;
      } catch (e) {
        isAuthValid = false;
      }
    }

    if (!isAuthValid) {
      const response = NextResponse.redirect(new URL(ROUTE_RULES.client.loginPath, request.url));
      response.cookies.delete(ROUTE_RULES.client.cookieKey);
      return response;
    }
  }

  // 已认证用户访问登录页时跳转至仪表盘
  if (pathname === ROUTE_RULES.staff.loginPath) {
    const staffCookie = request.cookies.get(ROUTE_RULES.staff.cookieKey)?.value;
    if (staffCookie) {
      try {
        const userData = JSON.parse(staffCookie);
        if (Date.now() <= userData.expiredAt) {
          return NextResponse.redirect(new URL('/staff', request.url));
        }
      } catch (e) {}
    }
  }

  if (pathname === ROUTE_RULES.client.loginPath) {
    const clientCookie = request.cookies.get(ROUTE_RULES.client.cookieKey)?.value;
    if (clientCookie) {
      try {
        const userData = JSON.parse(clientCookie);
        if (Date.now() <= userData.expiredAt) {
          return NextResponse.redirect(new URL('/client', request.url));
        }
      } catch (e) {}
    }
  }

  return NextResponse.next();
}

// 指定中间件生效的路由范围
export const config = {
  matcher: ['/staff/:path*', '/client/:path*', '/auth/staff', '/auth/client']
};

2. 移除客户端ProtectedRoutes组件

中间件会在页面渲染前完成拦截,完全不需要客户端组件做二次校验,直接去掉布局文件中的ProtectedRoutes包裹:

修改后的staff/layout.tsx:

'use client';

import StaffMenu from '@/app/components/dashboard/staff-menu';

export default function StaffLayout({ children }: { children: React.ReactNode }) {
  return (
    <div className="min-h-full">
      <StaffMenu />
      <main>
        <div className="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">
          {children}
        </div>
      </main>
    </div>
  );
}

client/layout.tsx做同样修改即可。

3. 清理冗余代码

  • 删除staff/middleware.ts和client/middleware.ts
  • 删除lib/router/staff/ProtectedRoutes.tsx和lib/router/client/ProtectedRoutes.tsx

优化说明

  • 中间件在边缘/服务器端执行,请求到达页面之前就完成重定向,彻底解决闪显问题
  • 统一路由与Cookie规则,避免逻辑不一致导致的权限漏洞
  • 移除客户端冗余校验,减少不必要的客户端渲染逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 05:37:31