Next.js基于角色的组件访问控制:isAdmin校验始终不返回true
问题分析与解决方案
核心问题拆解
- 异步函数判断失效:
checkAdminUser是异步函数,调用后返回Promise对象。直接在if条件中使用!checkAdminUser(...)时,Promise作为对象属于真值,导致!Promise始终为false;同时组件同步渲染时,异步请求还未完成,无法及时获取权限结果,权限逻辑完全失效。 - 空值未处理:
session.user?.email可能为undefined,会导致API请求URL变为/api/user/undefined,返回错误结果。 - 前端校验不安全:前端权限判断可通过修改代码绕过,必须在服务器端做最终校验。
方案一:前端异步权限校验(临时辅助方案)
通过React状态管理处理异步校验逻辑,同时处理加载状态和空值:
const Dashboard: React.FC<Props> = (props) => { const { data: session, status } = useSession(); const [isAdmin, setIsAdmin] = useState<boolean | null>(null); // null表示校验中 useEffect(() => { const verifyAdmin = async () => { if (!session?.user?.email) { setIsAdmin(false); return; } try { // 编码邮箱避免特殊字符破坏URL const encodedEmail = encodeURIComponent(session.user.email); const result = await fetch(`/api/user/${encodedEmail}`, { method: "GET" }); if (!result.ok) { setIsAdmin(false); return; } const user = await result.json(); setIsAdmin(user.isAdmin === 1); } catch (error) { console.error(error); setIsAdmin(false); } }; if (status === "authenticated") verifyAdmin(); else if (status === "unauthenticated") setIsAdmin(false); }, [session, status]); // 加载状态处理 if (status === "loading" || isAdmin === null) { return <Layout><div>加载中...</div></Layout>; } // 权限判断 if (!session || !isAdmin) { return ( <Layout> <AccessDenied /> </Layout> ); } return ( <Layout> {/* 后台布局代码 */} </Layout> ); };
方案二:服务器端权限校验(推荐,安全高效)
直接在getServerSideProps中完成权限校验,避免前端绕开,同时减少API调用开销:
export const getServerSideProps: GetServerSideProps = async (context) => { // 获取会话信息 const session = await getSession(context); // 无会话直接跳转权限拒绝页面 if (!session?.user?.email) { return { redirect: { destination: "/access-denied", permanent: false, }, }; } // 服务器端直接查询用户权限(无需调用API) const user = await prisma.user.findUnique({ where: { email: session.user.email }, select: { isAdmin: true }, }); // 非管理员跳转 if (!user || user.isAdmin !== 1) { return { redirect: { destination: "/access-denied", permanent: false, }, }; } // 获取仪表盘数据 const dashboards = await prisma.dashboard.findMany({ orderBy: { id: "asc" }, }); return { props: { dashboards: JSON.parse(JSON.stringify(dashboards)) }, }; }; const Dashboard: React.FC<Props> = (props) => { // 能进入此处的均为已通过校验的管理员 return ( <Layout> {/* 后台布局代码 */} </Layout> ); };
内容的提问来源于stack exchange,提问作者Marius
相关产品推荐
相关产品推荐

