Next.js URL重写需求:隐藏角色实现/admin/dashboard映射到/dashboard
解决方案:隐藏URL角色标识并兼容客户端/服务端路由
1. 统一页面入口:创建共享的/dashboard页面
在pages目录下新建dashboard.js(或dashboard/index.js),作为所有角色访问/dashboard的统一入口,根据用户角色动态渲染对应组件:
// pages/dashboard.js import { useEffect, useState } from 'react'; import { useRouter } from 'next/router'; export default function Dashboard({ userRole }) { const router = useRouter(); const [RoleComponent, setRoleComponent] = useState(null); useEffect(() => { if (!userRole) { router.push('/login'); return; } // 根据角色动态导入对应组件 const loadComponent = async () => { let component; if (userRole === 'admin') { component = (await import('../admin/dashboard')).default; } else if (userRole === 'employee') { component = (await import('../employee/dashboard')).default; } else { router.push('/403'); return; } setRoleComponent(component); }; loadComponent(); }, [router, userRole]); if (!RoleComponent) { return <div>加载中...</div>; } return <RoleComponent />; } // 服务端安全获取用户角色(优先用此方式,避免客户端篡改) export async function getServerSideProps(context) { // 从请求的cookie/session中获取用户角色,示例逻辑需根据你的鉴权方式调整 const userRole = context.req.cookies.userRole; if (!userRole) { return { redirect: { destination: '/login', permanent: false, }, }; } return { props: { userRole }, }; }
2. 修复客户端Link跳转问题
之前原生Link跳转无效,是因为Next.js客户端路由会优先匹配pages目录结构。现在有了pages/dashboard.js,直接用Link跳转/dashboard即可:
import Link from 'next/link'; // 在任意页面使用 <Link href="/dashboard"> <a>进入控制台</a> </Link>
代码中路由跳转直接调用router.push('/dashboard'),无需拼接角色路径。
3. 用Middleware做权限与路由控制
创建middleware.js(Next.js 12+),禁止直接访问带角色标识的路由,同时校验用户权限:
// middleware.js import { NextResponse } from 'next/server'; export function middleware(request) { const userRole = request.cookies.get('userRole')?.value; const pathname = request.nextUrl.pathname; // 禁止直接访问/admin或/employee开头的路由,强制跳转至/dashboard if (pathname.startsWith('/admin/') || pathname.startsWith('/employee/')) { // 校验角色是否匹配,不匹配则跳转至403 if ((pathname.startsWith('/admin/') && userRole !== 'admin') || (pathname.startsWith('/employee/') && userRole !== 'employee')) { return NextResponse.redirect(new URL('/403', request.url)); } // 角色匹配则跳转至/dashboard,隐藏角色URL return NextResponse.redirect(new URL('/dashboard', request.url)); } // 访问/dashboard时校验用户是否已登录并拥有角色 if (pathname === '/dashboard' && !userRole) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); } // 指定生效的路由范围 export const config = { matcher: ['/dashboard', '/admin/:path*', '/employee/:path*'], };
4. 可选:保留next.config.js的rewrites(兼容旧路由)
如果需要保留原有/admin/dashboard和/employee/dashboard的服务端映射,可添加rewrites规则确保刷新时正常:
// next.config.js module.exports = { async rewrites() { return [ { source: '/dashboard', destination: '/admin/dashboard', has: [{ type: 'cookie', key: 'userRole', value: 'admin' }], }, { source: '/dashboard', destination: '/employee/dashboard', has: [{ type: 'cookie', key: 'userRole', value: 'employee' }], }, ]; }, };
内容的提问来源于stack exchange,提问作者grayson
相关产品推荐
相关产品推荐

