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

Next.js 14手动输入URL被重定向至登录页的问题修复

解决Next.js客户端路由保护组件手动输入URL重定向异常问题

问题场景

编写的客户端路由保护组件代码如下:

'use client';

import { getUserId } from '#/utils/user';
import { redirect, usePathname } from 'next/navigation';

function ValidateProtectedRoute({ children }) {
  const pathname = usePathname();
  const userId = getUserId();
  if (!userId && pathname !== '/') {
    return redirect('/login');
  }
  return <div>{children}</div>;
}

export default ValidateProtectedRoute;

点击页面内链接(如首页、Jobs页)导航时功能正常,但手动输入URL(如https://localhost:3000/jobs)会被错误重定向到登录页,调试时能正常获取userId和pathname。

核心原因

手动输入URL时,页面会触发**服务器端渲染(SSR)或静态生成(SSG)**的初始加载流程:

  • 服务器端执行组件代码时,getUserId()通常依赖浏览器环境(比如从localStorage读取),此时服务器端无法获取到有效值,userId会返回null,触发重定向逻辑。
  • 虽然客户端hydrate完成后能正确拿到userId,但初始的服务器端重定向已经生效,导致页面被导向登录页。

解决方案

方案1:用服务器中间件实现路由保护(推荐)

路由保护属于安全逻辑,放在服务器端执行更可靠,且能覆盖所有请求场景(包括手动输入URL)。在项目根目录创建middleware.ts:

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

export function middleware(request: NextRequest) {
  // 从cookie中获取用户标识(根据实际存储方式调整)
  const userId = request.cookies.get('userId')?.value;
  const pathname = request.nextUrl.pathname;

  // 非首页、非登录页且未登录时,重定向到登录页
  if (!userId && pathname !== '/' && pathname !== '/login') {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  // 已登录状态下访问登录页,重定向到首页
  if (userId && pathname === '/login') {
    return NextResponse.redirect(new URL('/', request.url));
  }

  return NextResponse.next();
}

// 指定中间件作用的路由范围(排除静态资源、API等)
export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

方案2:修改客户端组件,处理初始加载状态

如果必须使用客户端组件,需等待客户端hydrate完成后再执行重定向逻辑:

'use client';

import { getUserId } from '#/utils/user';
import { redirect, usePathname } from 'next/navigation';
import { useEffect, useState } from 'react';

function ValidateProtectedRoute({ children }) {
  const pathname = usePathname();
  const [isHydrated, setIsHydrated] = useState(false);
  const userId = getUserId();

  // 标记客户端hydrate完成
  useEffect(() => {
    setIsHydrated(true);
  }, []);

  // 初始加载时不执行重定向,避免服务器端的错误判断
  if (!isHydrated) {
    return null; // 可替换为自定义加载动画
  }

  if (!userId && pathname !== '/') {
    redirect('/login');
  }
  return <div>{children}</div>;
}

export default ValidateProtectedRoute;

注意事项

  • 客户端组件方案仅为临时兼容方案,存在安全隐患(用户可通过修改客户端代码绕过保护),生产环境优先使用中间件方案。
  • 确保getUserId()的实现逻辑在服务器端和客户端环境下的行为一致,避免出现环境差异导致的判断错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 07:10:21