Next.js中间件用jose验证Access Token时陷入无限循环无法跳转登录页
Next.js中间件验证JWT陷入无限循环的问题解决
我在Next.js中间件中使用jose库验证Access Token时,遇到了一个问题:当代码进入catch块(比如Token无效或被篡改时),会陷入无限循环,无法正常跳转到登录页。尤其在用户修改浏览器中的Access Token时,这个问题特别明显。
我的代码如下:
import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; import { jwtVerify } from 'jose'; export default async function middleware(request: NextRequest) { const access = request.cookies.get("access")?.value; const url = request.url; const urlStartWithUrl = `${process.env.NEXT_PUBLIC_HOST_FRONTEND}/open-bo-account`; const redirectUrl = `${process.env.NEXT_PUBLIC_HOST_FRONTEND}/login/`; if (!access && request.nextUrl.pathname.startsWith('/open-bo-account')) { request.cookies.clear() return NextResponse.redirect(new URL('/login', request.url)); } else if (access) { try { const secret = new TextEncoder().encode( "secret key" ); const decodedToken = await jwtVerify(access, secret); if (decodedToken) { return NextResponse.next(); } request.cookies.clear(); return NextResponse.redirect(new URL('/login', request.url)); } catch (error) { const cookiesBeforeClear = request.cookies.getAll(); console.log("Cookies before clear:", cookiesBeforeClear); request.cookies.clear(); const cookiesAfterClear = request.cookies.getAll(); console.log("Cookies after clear:", cookiesAfterClear); return NextResponse.redirect(new URL('/login', request.url)); } } }
问题原因
- 错误的cookie清除方式:
request.cookies.clear()只是修改了请求对象中的临时cookie数据,并不会向浏览器发送清除cookie的响应头。这导致浏览器中的accesscookie始终存在,每次重定向后中间件都会读取到无效Token,重复进入catch块触发循环。 - 未排除登录页路由:中间件默认拦截所有路由请求,包括重定向后的
/login页面。即使cookie被正确清除,原逻辑也未对登录页做放行处理,进一步加剧循环风险。
解决方案
- 正确清除浏览器cookie:通过
NextResponse对象的cookie方法设置过期或直接删除,让浏览器真正移除无效cookie。 - 排除登录页路由:在中间件开头判断如果是登录页请求,直接放行,避免拦截重定向后的请求。
- 优化路由保护逻辑:仅对需要验证的路由(如
/open-bo-account)执行Token验证,减少不必要的处理。
修改后的代码:
import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; import { jwtVerify } from 'jose'; export default async function middleware(request: NextRequest) { // 排除登录页,直接放行 if (request.nextUrl.pathname.startsWith('/login')) { return NextResponse.next(); } const access = request.cookies.get("access")?.value; const protectedPath = '/open-bo-account'; // 仅处理需要保护的路由 if (request.nextUrl.pathname.startsWith(protectedPath)) { if (!access) { // 清除所有cookie并跳转到登录页 const response = NextResponse.redirect(new URL('/login', request.url)); response.cookies.clear(); return response; } try { // 建议将密钥放在环境变量中,不要硬编码 const secret = new TextEncoder().encode(process.env.JWT_SECRET || "secret key"); await jwtVerify(access, secret); // Token验证通过,放行 return NextResponse.next(); } catch (error) { console.error("Token验证失败:", error); // 清除无效的access cookie并跳转到登录页 const response = NextResponse.redirect(new URL('/login', request.url)); response.cookies.delete("access"); // 如有refresh token也可一并删除 // response.cookies.delete("refresh"); return response; } } // 其他路由直接放行 return NextResponse.next(); }
补充说明
- 务必将JWT密钥存入环境变量(如
JWT_SECRET),避免硬编码泄露风险。 - 使用
response.cookies.delete("access")精准清除单个cookie,比clear()更灵活,适合只需要移除无效Token的场景。 - 先排除登录页的逻辑,确保重定向后不会被中间件再次拦截,彻底切断循环链路。
内容的提问来源于stack exchange,提问作者sourav singha
相关产品推荐
相关产品推荐

