如何用@supabase/auth-helpers-nextjs完善Next.js登录重定向?
解决方案:Next.js + Supabase 认证路由最佳实践
针对你遇到的middleware失效、路由守卫闪烁及URL不更新问题,以下是分步解决的最佳实践:
一、修复Middleware的未认证重访问题
你的middleware逻辑存在两处优化点:
- 未处理已登录用户访问登录页的场景
- 逻辑判断边界不够清晰
修改middleware.ts如下:
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs' import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export async function middleware(req: NextRequest) { const res = NextResponse.next() const supabase = createMiddlewareSupabaseClient({ req, res }) const { data: { session } } = await supabase.auth.getSession() // 已登录用户访问登录页时,重定向到主页 if (session && req.nextUrl.pathname === '/') { return NextResponse.redirect(new URL('/posts', req.url)) } // 未登录用户访问受保护路由时,重定向到登录页 const protectedRoutes = ['/posts', '/createpost', '/profile'] if (!session && protectedRoutes.includes(req.nextUrl.pathname)) { return NextResponse.redirect(new URL('/', req.url)) } return res } export const config = { matcher: ['/', '/posts', '/createpost', '/profile'], // 包含登录页,处理已登录跳转 }
二、解决登出后的客户端重定向问题
Middleware仅在页面初始请求时触发,登出属于客户端操作,需手动触发路由跳转:
修改你的登出按钮逻辑:
import { useSupabaseClient } from '@supabase/auth-helpers-react' import { useRouter } from 'next/router' export function LogoutButton() { const supabase = useSupabaseClient() const router = useRouter() const handleLogout = async () => { await supabase.auth.signOut() // 登出后强制跳转到登录页,触发完整的认证校验流程 router.push('/') } return <button onClick={handleLogout}>登出</button> }
三、重构RouteGuard,解决闪烁和URL不更新问题
你的RouteGuard存在两个核心问题:重复实现auth状态监听、未触发客户端路由跳转导致URL不匹配。
1. 删除冗余的useAuth.js
直接使用@supabase/auth-helpers-react提供的useSession,它已内置加载状态和实时auth监听,无需手动实现。
2. 重构RouteGuard组件
import { useSession } from '@supabase/auth-helpers-react' import { useRouter } from 'next/router' import { useEffect } from 'react' const RouteGuard = ({ children }: { children: React.ReactElement }) => { const { data: session, isLoading } = useSession() const router = useRouter() const protectedRoutes = ['/posts', '/createpost', '/profile'] // 加载状态显示加载UI,避免页面闪烁 if (isLoading) { return <div className="flex items-center justify-center min-h-screen">加载中...</div> } // 未认证且当前在受保护路由,强制跳转到登录页 useEffect(() => { if (!session && protectedRoutes.includes(router.pathname)) { router.push('/') } }, [session, router.pathname]) // 已认证或当前在登录页,渲染对应内容 if (session || router.pathname === '/') { return children } // 过渡状态:避免渲染空白 return null }
3. 优化_app.tsx
去掉冗余的自定义auth逻辑,简化为:
function MyApp({ Component, pageProps, }: AppProps<{ initialSession: Session }>) { const [supabaseClient] = useState(() => createBrowserSupabaseClient()) const queryClient = new QueryClient() return ( <SessionContextProvider supabaseClient={supabaseClient} initialSession={pageProps.initialSession} > <QueryClientProvider client={queryClient}> <RouteGuard> <Component {...pageProps} /> </RouteGuard> </QueryClientProvider> </SessionContextProvider> ) } export default MyApp
四、优化Login页面逻辑
避免在组件渲染阶段直接调用路由跳转,改用useEffect处理状态变化:
import type { NextPage } from 'next' import { Auth, ThemeMinimal } from '@supabase/auth-ui-react' import { useSession, useSupabaseClient } from '@supabase/auth-helpers-react' import { useRouter } from 'next/router' import { useEffect } from 'react' import { CodeBracketIcon, LifebuoyIcon, CurrencyDollarIcon } from '@heroicons/react/20/solid' export const Login: NextPage = () => { const { data: session } = useSession() const router = useRouter() const supabase = useSupabaseClient() useEffect(() => { if (session) { router.push('/posts') } }, [session, router]) return ( <div className="mt-12"> <div className="flex min-h-full"> <div className="mx-auto w-full max-w-sm lg:w-96"> <div> <CodeBracketIcon className="h-12" /> <h2 className="mt-6 text-3xl font-bold tracking-tight text-gray-900">Code Market</h2> <div className="mt-2 text-sm text-gray-600 "> <div className="flex items-center pb-1 gap-1"> <LifebuoyIcon className="h-4" /> Offer money for coding help </div> <div className="flex items-center gap-1"> <CurrencyDollarIcon className="h-4" /> Make money helping other coders </div> </div> </div> <div className="mt-8"> <Auth providers={['github']} supabaseClient={supabase} appearance={{ theme: ThemeMinimal }} /> </div> </div> </div> </div> ) }
关键说明
- Middleware + 客户端守卫结合:Middleware处理初始请求重定向,客户端守卫处理路由切换和auth状态变化后的重定向,覆盖所有场景
- 复用官方状态管理:
useSession会实时监听auth状态变化(登出、token过期等),无需手动维护用户状态 - 加载状态控制:利用
isLoading状态显示加载UI,彻底解决页面闪烁问题 - URL一致性保障:未认证时强制跳转到登录页,确保URL与页面内容完全匹配
内容的提问来源于stack exchange,提问作者Jar
相关产品推荐
相关产品推荐

