我正在使用 Next.js App Router 并实现了中间件来检查访问令牌是否存在。如果用户尝试在没有令牌的情况下访问根 (/) 以外的任何页面或与登录相关的页面,我会将 returnURL 添加到搜索参数中,以便他们可以在登录后重定向回该页面。但是,这方法并未按预期发挥作用。为了处理登录过程,我使用了react-hook-form以及tanstack-query的useMutation。
const returnURL = useSearchParams().get('returnURL') || '/';
//submit handler
const onSubmit: SubmitHandler<FieldValues> = () => {
postSigninMutate(
{ username: payload.username, password: payload.password },
{
onSuccess: async (res) => {
const { response } = res;
const authorizationToken = response.headers.get('authorization-token');
const expiresAt = response.headers.get('authorization-token-expired-at');
const refreshToken = response.headers.get('refresh-token');
if (authorizationToken && expiresAt && refreshToken) {
Cookies.set('authorization-token', authorizationToken, { secure: true });
Cookies.set('expires-at', expiresAt);
Cookies.set('refresh-token', refreshToken, { secure: true });
}
router.push(returnURL);
},
onError: (err) => alert(err)
}
);
};
<form onSubmit={handleSubmit(onSubmit)} className="flex-col-start m-0 w-80 gap-6 pt-10">
...
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
const url = request.nextUrl.pathname;
const accessToken = request.cookies.get('authorization-token');
console.log(accessToken ? 'yes' : 'no');
console.log(url);
if (url === '/') {
const response = NextResponse.next();
response.headers.set('X-Skip-Icons', 'true');
return response;
}
if (
accessToken &&
(url === '/signup' ||
url === '/login' ||
url === '/kakaoLogin' ||
url === '/find-password-email' ||
url === '/find-password-question' ||
url === '/find-password-newpassword')
) {
if (request.nextUrl.searchParams.has('returnURL')) {
return NextResponse.redirect(new URL(request.nextUrl.searchParams.get('returnURL') || '/', request.url));
}
console.log('here');
return NextResponse.redirect(new URL('/', request.url));
}
if (
!accessToken &&
url !== '/signup' &&
url !== '/login' &&
url !== '/kakaoLogin' &&
url !== '/find-password-email' &&
url !== '/find-password-question' &&
url !== '/find-password-newpassword'
) {
const redirectURL = new URL('/login', request.url);
const returnURL = request.nextUrl.pathname + request.nextUrl.search;
redirectURL.searchParams.set('returnURL', returnURL);
const response = NextResponse.redirect(redirectURL);
return response;
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico|icons/favicon.ico|icon).*)']
};
我怀疑该问题是由于页面缓存以及客户端和服务器上的 cookie 更新不匹配造成的。调用router.refresh()后,中间件触发,页面更新,但地址栏中的URL没有变化。然后我在 router.refresh() 之后添加了一个 setTimeout 来延迟 router.push(returnURL) ,虽然这个解决方案有效,但它让我怀疑这是否是正确的方法。
但是,当我按后退按钮时,我仍然可以访问登录页面,这在成功登录后不应该发生。
router.refresh();
setTimeout(() => {
router.push(returnURL);
}, 1000);
解决登录后
router.push()
问题:
router.replace()
而不是 router.push()
来防止导航回登录页面。router.replace(returnURL);
await router.refresh();
router.replace(returnURL);
确保在重定向之前设置 cookie,以避免竞争情况。
if (!accessToken && !['/login', '/signup'].includes(url)) {
const redirectURL = new URL('/login', request.url);
redirectURL.searchParams.set('returnURL', request.nextUrl.pathname);
return NextResponse.redirect(redirectURL);
}
这应该可以解决问题。