Next-Auth authconfig扩展:基于isAdmin的路由跳转与权限校验
解决Next-Auth用户信息不全及登录后角色跳转问题
1. 补全用户信息存储逻辑
要让auth对象能获取到完整用户字段,核心是在JWT和Session流程中携带自定义字段:
修改authconfig.ts的CredentialsProvider配置
在authorize回调中返回包含所有字段的用户对象,再通过jwt和session回调将字段同步到token和session中:
import NextAuth from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; // 模拟数据库查询用户的方法,替换成你的实际逻辑 const fetchUserByEmail = async (email: string | undefined) => { // 示例数据,实际从数据库读取 return email === "admin@example.com" ? { id: "1", email: "admin@example.com", username: "admin", img: "/admin.jpg", isAdmin: true, password: "123456" } : { id: "2", email: "user@example.com", username: "user", img: "/user.jpg", isAdmin: false, password: "123456" }; }; export const authOptions = { providers: [ CredentialsProvider({ name: "Credentials", credentials: { email: { label: "邮箱", type: "email" }, password: { label: "密码", type: "password" } }, async authorize(credentials) { if (!credentials?.email || !credentials?.password) return null; const user = await fetchUserByEmail(credentials.email); if (!user) return null; // 实际项目请用bcrypt等加密库验证密码,这里仅做示例 const isPasswordValid = credentials.password === user.password; if (!isPasswordValid) return null; // 返回完整用户对象,包含需要的所有字段 return { id: user.id, email: user.email, username: user.username, img: user.img, isAdmin: user.isAdmin }; } }) ], // CredentialsProvider推荐用JWT存储会话 session: { strategy: "jwt" }, callbacks: { // 将自定义字段写入JWT token async jwt({ token, user }) { if (user) { token.id = user.id; token.username = user.username; token.img = user.img; token.isAdmin = user.isAdmin; } return token; }, // 将token中的字段映射到session.user,让前端/auth对象能读取到 async session({ session, token }) { if (session.user) { session.user.id = token.id as string; session.user.username = token.username as string; session.user.img = token.img as string; session.user.isAdmin = token.isAdmin as boolean; } return session; } } }; export default NextAuth(authOptions);
2. 基于isAdmin做权限校验
现在auth.user已包含isAdmin字段,可在authorized回调中实现路由权限控制:
// 在authOptions的callbacks中添加authorized回调 async authorized({ auth, request }) { const isLoggedIn = !!auth?.user; const targetPath = request.nextUrl.pathname; // 管理员仅能访问dashboard相关路由 if (targetPath.startsWith("/dashboard")) { return auth?.user?.isAdmin === true; } // 普通用户仅能访问store相关路由 if (targetPath.startsWith("/store")) { return isLoggedIn && auth?.user?.isAdmin === false; } // 未登录用户跳转登录页 return isLoggedIn; }
3. 登录后按角色跳转
有两种常用实现方式,按需选择:
方式1:前端登录组件手动控制跳转
在登录页面调用signIn时关闭自动跳转,拿到会话信息后手动跳转:
import { signIn, getSession } from "next-auth/react"; const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); const email = (e.target as any).email.value; const password = (e.target as any).password.value; const result = await signIn("credentials", { email, password, redirect: false // 关闭自动跳转 }); if (result?.ok) { const session = await getSession(); if (session?.user?.isAdmin) { window.location.href = "/dashboard"; } else { window.location.href = "/store"; } } };
方式2:用Middleware统一处理跳转
在middleware.ts中拦截已登录用户的请求,自动跳转到对应角色页面:
import { withAuth } from "next-auth/middleware"; import { NextResponse } from "next/server"; export default withAuth( function middleware(req) { const isAdmin = req.nextauth.token?.isAdmin; const currentPath = req.nextUrl.pathname; // 管理员不在dashboard页面则跳转 if (isAdmin && !currentPath.startsWith("/dashboard")) { return NextResponse.redirect(new URL("/dashboard", req.url)); } // 普通用户不在store页面则跳转 if (!isAdmin && !currentPath.startsWith("/store")) { return NextResponse.redirect(new URL("/store", req.url)); } }, { callbacks: { authorized: ({ token }) => !!token // 仅拦截已登录用户 } } ); // 指定需要拦截的路由 export const config = { matcher: ["/", "/dashboard/:path*", "/store/:path*"] };
核心注意事项
- 必须设置
session.strategy: "jwt",CredentialsProvider默认不支持数据库会话,只有JWT能携带自定义字段。 jwt和session回调的字段映射必须一一对应,否则auth.user无法获取到非默认字段。- 权限校验和跳转逻辑可结合使用,比如用
authorized控制路由访问权限,用middleware处理自动跳转。
内容的提问来源于stack exchange,提问作者Tejendra Singh Rajawat
相关产品推荐
相关产品推荐

