NextAuth与Middleware配置问题:登录后仍可访问登录页
问题解决:已登录状态仍可访问登录页 + Middleware未触发
核心问题分析
- Middleware未触发登录页:NextAuth 的
/api/auth/*路由默认由自身逻辑优先处理,自定义 Middleware 不会生效;同时withAuth默认跳过登录页的授权检查,不会执行内部回调。 - 权限逻辑隐患:原代码中对
/api/auth的拦截会导致未登录用户重定向到登录页时触发循环跳转,且用includes判断路径容易误匹配。
修复步骤
1. 修改 Middleware 配置,强制触发登录页检查
添加路由匹配规则,调整 withAuth 配置让所有请求进入自定义逻辑:
import { withAuth, NextRequest, NextResponse } from "next-auth/middleware"; export default withAuth( async function middleware(request: NextRequest) { const { token } = request.nextauth; console.log('on middleware'); // 已登录用户访问登录页,直接跳转对应仪表盘 if (token && request.nextUrl.pathname === "/api/auth/signin") { const target = token.user.role === "PARTNER" ? "/cms/partner/dashboard" : "/cms/admin/dashboard"; return NextResponse.redirect(new URL(target, request.url)); } // 未登录用户访问权限页,跳转登录页 if (!token && request.nextUrl.pathname !== "/api/auth/signin") { return NextResponse.redirect(new URL("/api/auth/signin", request.url)); } // 角色权限校验 if (token) { if (token.user.role === "PARTNER" && request.nextUrl.pathname.startsWith("/cms/admin")) { return NextResponse.redirect(new URL("/cms/partner/dashboard", request.url)); } if (token.user.role === "ADMIN" && request.nextUrl.pathname.startsWith("/cms/partner")) { return NextResponse.redirect(new URL("/cms/admin/dashboard", request.url)); } } }, { secret: process.env.NEXTAUTH_SECRET, // 强制所有匹配路径进入自定义Middleware callbacks: { authorized: () => true, }, } ); // 指定需要Middleware处理的路由 export const config = { matcher: [ "/api/auth/signin", "/cms/admin/:path*", "/cms/partner/:path*", ], };
2. 优化 NextAuth 配置(类型安全+错误处理)
import NextAuth, { AuthOptions } from "next-auth"; import CredentialsProvider from "next-auth/providers/credentials"; // 定义用户类型 interface AppUser { id: string; role: "ADMIN" | "PARTNER"; token: string; } const handler = NextAuth({ session: { strategy: "jwt", }, providers: [ CredentialsProvider({ name: "Sign in", credentials: { username: { label: "Username", type: "text" }, password: { label: "Password", type: "password" }, }, async authorize(credentials) { if (!credentials?.username || !credentials?.password) return null; try { const { data } = await api.post("/cms/auth/login", { username: credentials.username, password: credentials.password, }); return { id: Math.random().toString(), role: data.data.role, token: data.data.token, } as AppUser; } catch { // 登录失败返回null,NextAuth会提示错误 return null; } }, }), ], callbacks: { async session({ session, token }) { session.user = token.user as AppUser; return session; }, async jwt({ token, user }) { if (user) token.user = user; return token; }, }, secret: process.env.NEXTAUTH_SECRET, } satisfies AuthOptions); export { handler as GET, handler as POST };
关键优化点
- 路由匹配:用
matcher明确指定需要处理的路由,确保登录页被覆盖。 - 强制触发:
authorized: () => true让withAuth不再跳过任何路径,完全由自定义逻辑控制权限。 - 路径判断:用
startsWith替代includes,避免误匹配无关路径。 - 错误处理:添加登录失败的异常捕获,避免程序崩溃。
内容的提问来源于stack exchange,提问作者Juan Alfonsus
相关产品推荐
相关产品推荐

