Next.js Middleware重定向未达预期 路由鉴权逻辑异常排查
Next.js Middleware 重定向失效修复方案
核心问题
原代码重定向逻辑不生效的核心原因有4个:
- 直接用
req.url做路由匹配:req.url是带协议、域名、端口的完整绝对地址,存在validRoutes里的是相对路径,字符串匹配永远无法命中 - 逻辑分支冗余:
if(url包含login路径)和else if(url不包含login路径)已经覆盖了所有可能场景,最后的else分支是永远走不到的死代码 - 跳转/重写传参不规范:直接传相对路径给
NextResponse.redirect/rewrite,在部分Next.js版本、非根域名部署场景下会解析失败 - 没做路径规范化:带查询参数、尾斜杠的合法路径会被直接判定为非法路由,匹配逻辑鲁棒性差
修复后完整代码
import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' // 项目基础路径,适配basePath部署场景 const base_url = process.env.NEXT_PUBLIC_BASE_PATH || '' // 无需登录即可访问的公开路由 const publicRoutes = [ `${base_url}/login`, `${base_url}/404`, `${base_url}/api/auth/user`, `${base_url}/api/auth/logout`, `${base_url}/api/auth/setpass` ] // 登录后可访问的受保护路由,后续可按角色拆分权限映射表 const protectedRoutes = [ `${base_url}/`, // 补充业务侧受保护路由,比如`${base_url}/dashboard`、`${base_url}/user/profile`等 ] // 所有合法路由汇总,用于404判定 const allValidRoutes = [...publicRoutes, ...protectedRoutes] export function middleware(req: NextRequest) { const { cookies, nextUrl } = req // 获取规范化的当前路径,自动剔除域名、查询参数、锚点 const currentPath = nextUrl.pathname const jwt = cookies.get('user')?.value // 页面访问监控埋点位置,可在此处上报访问路径、用户token状态、UA等信息,支撑后续角色权限审计、行为分析 // 示例:await fetch(`${base_url}/api/monitor/track`, { method: 'POST', body: JSON.stringify({ path: currentPath, hasToken: !!jwt, timestamp: Date.now() }) }) // 场景1:已登录用户访问登录页,直接跳转首页 if (currentPath === `${base_url}/login`) { if (jwt) return NextResponse.redirect(new URL(`${base_url}/`, req.url)) return NextResponse.next() } // 路径合法性校验:不区分登录态,非法路径统一重写到404页 const isRouteValid = allValidRoutes.some(route => { const normalizedRoute = route.endsWith('/') ? route.slice(0, -1) : route const normalizedPath = currentPath.endsWith('/') ? currentPath.slice(0, -1) : currentPath return normalizedRoute === normalizedPath }) if (!isRouteValid) { return NextResponse.rewrite(new URL(`${base_url}/404`, req.url)) } // 公开路由直接放行 const isPublicRoute = publicRoutes.some(route => { const normalizedRoute = route.endsWith('/') ? route.slice(0, -1) : route const normalizedPath = currentPath.endsWith('/') ? currentPath.slice(0, -1) : currentPath return normalizedRoute === normalizedPath }) if (isPublicRoute) return NextResponse.next() // 受保护路由登录态校验 if (jwt) { // 角色权限扩展点:在此处解析jwt拿到用户角色,判断当前路径是否在该角色的可访问路由列表内,无权限可跳403页 return NextResponse.next() } // 未登录访问受保护路由,拼接回跳参数后跳登录页 const loginUrl = new URL(`${base_url}/login`, req.url) loginUrl.searchParams.set('redirect', currentPath) return NextResponse.redirect(loginUrl) } // 配置middleware生效范围,排除静态资源、Next.js内部路径,减少无效执行 export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'], }
关键调整说明
- 路由匹配统一用
req.nextUrl.pathname获取纯路径,从根源解决完整URL和相对路径匹配失败的问题 - 重构逻辑分层,按「登录页特殊处理→路由合法性校验→公开路由放行→登录态/权限校验」的顺序处理,没有死分支,逻辑可追溯
- 所有跳转、重写操作统一用
new URL(目标路径, req.url)生成标准URL对象,兼容各种部署场景,不会出现路径解析失败 - 增加路径规范化逻辑,自动忽略路径尾斜杠差异,避免合法路径被误拦截
- 预留了访问监控埋点、角色权限判断的扩展位置,直接在对应位置加业务代码即可支撑访问行为监控、角色权限管理的需求
- 增加matcher配置,跳过静态资源等不需要鉴权的路径,提升middleware运行性能
- 未登录跳转时自动携带原始访问路径参数,用户登录后可直接回跳到目标页,交互体验更好
内容的提问来源于stack exchange,提问作者Emmanuel Osei Mensah
相关产品推荐
相关产品推荐

