Next.js 14房产应用:管理端与用户端页面权限控制及目录结构设计
Next.js 14 房产应用权限控制实现方案
推荐目录结构
/app /auth /login page.tsx /dashboard # 管理端页面 layout.tsx /properties page.tsx /users page.tsx /client # 用户端页面 layout.tsx /home page.tsx /property-details [id] page.tsx /_components /auth SessionProviderWrapper.tsx middleware.ts layout.tsx /lib /auth session.ts # 会话处理工具函数
核心实现步骤
1. 实现会话与角色判断工具函数
在/lib/auth/session.ts中编写工具函数,用于获取当前用户会话和判断角色:
import { cookies } from 'next/headers'; // 假设登录后在cookie中存储用户信息,包含role字段('user' | 'admin') export async function getCurrentUser() { const cookieStore = cookies(); const userCookie = cookieStore.get('user'); if (!userCookie) return null; try { return JSON.parse(userCookie.value); } catch (err) { return null; } } export async function isAdmin() { const user = await getCurrentUser(); return user?.role === 'admin'; } export async function isLoggedIn() { const user = await getCurrentUser(); return !!user; }
2. 编写 Middleware 实现权限拦截
在项目根目录的middleware.ts中编写核心权限控制逻辑:
import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; import { getCurrentUser } from './lib/auth/session'; export async function middleware(request: NextRequest) { const path = request.nextUrl.pathname; // 定义路由规则 const isAuthRoute = path.startsWith('/auth/login'); const isDashboardRoute = path.startsWith('/dashboard'); const isClientRoute = path.startsWith('/client'); const user = await getCurrentUser(); // 未登录用户处理 if (!user) { // 非登录页跳转登录 if (!isAuthRoute) { return NextResponse.redirect(new URL('/auth/login', request.url)); } return NextResponse.next(); } // 已登录用户访问登录页,跳转对应首页 if (isAuthRoute) { return user.role === 'admin' ? NextResponse.redirect(new URL('/dashboard', request.url)) : NextResponse.redirect(new URL('/client/home', request.url)); } // 普通用户拦截管理端路由 if (isDashboardRoute) { if (user.role !== 'admin') { return NextResponse.redirect(new URL('/client/home', request.url)); } return NextResponse.next(); } // 用户端路由放行(管理员也可访问,需限制可调整) if (isClientRoute) { return NextResponse.next(); } // 其他路由默认放行 return NextResponse.next(); } // 指定Middleware生效的路由(排除静态资源和API) export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], };
3. 布局组件配合权限控制
- 根布局(/app/layout.tsx):全局引入会话提供者,确保页面能获取用户信息:
import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; import SessionProviderWrapper from './_components/auth/SessionProviderWrapper'; const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { title: '房产应用', description: '房产管理与用户服务平台', }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="zh-CN"> <body className={inter.className}> <SessionProviderWrapper>{children}</SessionProviderWrapper> </body> </html> ); }
- 管理端布局(/app/dashboard/layout.tsx):双重验证权限,渲染管理员专属导航:
import { getCurrentUser } from '@/lib/auth/session'; export default async function DashboardLayout({ children, }: { children: React.ReactNode; }) { const user = await getCurrentUser(); // 防止middleware被绕过,二次验证权限 if (user?.role !== 'admin') { return <div>无权限访问</div>; } return ( <div className="dashboard-container"> <nav>管理员导航:房产管理 | 用户管理</nav> <main>{children}</main> </div> ); }
- 用户端布局(/app/client/layout.tsx):渲染用户专属导航:
import { getCurrentUser } from '@/lib/auth/session'; export default async function ClientLayout({ children, }: { children: React.ReactNode; }) { const user = await getCurrentUser(); return ( <div className="client-container"> <nav>用户导航:首页 | 房产列表 | 我的收藏</nav> <main>{children}</main> </div> ); }
4. 登录页面示例(/app/auth/login/page.tsx)
登录成功后设置用户cookie,包含角色信息:
'use client'; import { useState } from 'react'; import { cookies } from 'next/headers'; import { useRouter } from 'next/navigation'; export default function LoginPage() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const router = useRouter(); const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); // 模拟登录请求,实际替换为后端接口 const mockUser = email.includes('admin') ? { id: 1, email, role: 'admin' } : { id: 2, email, role: 'user' }; // 设置安全cookie,生产环境需配置secure: true cookies().set('user', JSON.stringify(mockUser), { httpOnly: true, maxAge: 60 * 60 * 24, // 有效期1天 path: '/', }); // 根据角色跳转对应页面 router.push(mockUser.role === 'admin' ? '/dashboard' : '/client/home'); }; return ( <div className="login-container"> <form onSubmit={handleLogin}> <input type="email" placeholder="邮箱" value={email} onChange={(e) => setEmail(e.target.value)} required /> <input type="password" placeholder="密码" value={password} onChange={(e) => setPassword(e.target.value)} required /> <button type="submit">登录</button> </form> </div> ); }
关键注意事项
- Cookie安全:生产环境务必开启
secure: true和sameSite: 'strict',防范CSRF攻击。 - 双重验证:布局和页面中加入权限判断,避免Middleware被绕过。
- 会话刷新:使用JWT时需处理token过期自动刷新逻辑。
内容的提问来源于stack exchange,提问作者khalid mansoor
相关产品推荐
相关产品推荐

