Next.js如何实现全页面通用的服务端Cookie存在性检测?
可行实现方案
因为你使用的是http-only类型的Cookie,前端JS无法直接读取Cookie值,所以校验逻辑推荐优先在服务端侧执行,以下是两种适配不同场景的成熟方案:
方案1:Next.js中间件(全版本通用,最推荐)
中间件运行在服务端,会在所有页面请求触发前执行,不管是静态生成页面还是服务端渲染页面、用户从任意入口进入站点都能被拦截,完全不需要改动现有业务页面代码。
- 在项目根目录(和
pages文件夹同级)新建middleware.js文件 - 写入如下校验逻辑:
import { NextResponse } from 'next/server' // 配置不需要登录就能访问的白名单:登录注册页、静态资源、公开接口等 const publicRoutes = [ '/login', '/register', '/_next', '/favicon.ico', '/api/public' ] export default function middleware(req) { const { pathname } = req.nextUrl // 白名单路径直接放行 if (publicRoutes.some(route => pathname.startsWith(route))) { return NextResponse.next() } // 服务端直接读取请求头中的Cookie,判断认证token是否存在 const authToken = req.cookies.get('auth_token')?.value if (!authToken) { // 未登录则跳转到登录页,带上当前页面路径作为登录后的回调参数 const loginUrl = new URL('/login', req.url) loginUrl.searchParams.set('redirect', req.nextUrl.pathname + req.nextUrl.search) return NextResponse.redirect(loginUrl) } // 校验通过,继续正常请求 return NextResponse.next() } // 配置中间件生效范围,匹配全站所有路径 export const config = { matcher: '/:path*', }
注意:上述逻辑仅判断Cookie是否存在,如果需要校验token有效性、是否过期,可在中间件中向后端认证接口发起请求,携带当前Cookie完成校验后再放行,避免无效token通行。
方案2:Pages Router场景下修复_app.js的getInitialProps逻辑
你之前用getInitialProps不生效大概率是因为项目中存在使用getStaticProps的静态生成页面,这类页面的服务端请求默认不会触发_app的getInitialProps,可以配合客户端路由拦截逻辑补全覆盖:
import App from 'next/app' import { useRouter } from 'next/router' import { useEffect } from 'react' const publicRoutes = ['/login', '/register', '/about'] function MyApp({ Component, pageProps, isAuthenticated }) { const router = useRouter() // 客户端侧路由跳转时的校验逻辑,覆盖静态页跳转场景 useEffect(() => { const handleRouteChange = async (url) => { if (publicRoutes.includes(url)) return // 调用后端轻量校验接口,请求会自动携带http-only Cookie,返回登录状态 const res = await fetch('/api/auth/check') if (!res.ok) { router.push('/login?redirect=' + encodeURIComponent(router.asPath)) } } router.events.on('routeChangeStart', handleRouteChange) return () => router.events.off('routeChangeStart', handleRouteChange) }, [router]) if (!isAuthenticated && !publicRoutes.includes(router.pathname)) { router.push('/login?redirect=' + encodeURIComponent(router.asPath)) return null } return <Component {...pageProps} /> } // 服务端侧校验逻辑,覆盖动态页面首屏请求 MyApp.getInitialProps = async (appContext) => { const appProps = await App.getInitialProps(appContext) const { req, res } = appContext.ctx let isAuthenticated = false if (req) { const cookies = req.headers.cookie || '' isAuthenticated = cookies.includes('auth_token=') if (!isAuthenticated && !publicRoutes.includes(appContext.ctx.pathname)) { res?.writeHead(302, { Location: `/login?redirect=${encodeURIComponent(appContext.ctx.asPath)}` }) res?.end() return { ...appProps, isAuthenticated: false } } } return { ...appProps, isAuthenticated } } export default MyApp
内容的提问来源于stack exchange,提问作者RItesh Gupta
相关产品推荐
相关产品推荐

