Next.js生产环境鉴权跳转受保护页面报客户端异常
问题修复方案
根因定位
- 直接触发客户端白屏报错的原因:
useSession钩子的错误处理缺失空值校验。当接口返回500错误、跨域拦截、网络异常等场景时,Axios抛出的错误对象上response字段为undefined,直接读取error.response.data.redirectUrl会触发未捕获的JS异常,对应你看到的Application error: a client-side exception has occurred报错。 - Cookie无法被服务端读取的核心原因:axios实例使用了拼接
process.env.HOST的绝对路径作为baseURL,生产环境下只要HOST配置的协议、域名、端口和实际站点访问地址不一致,请求就会变成跨域请求。跨域场景下如果sid Cookie没有配置对应跨域策略,浏览器不会在请求中携带该Cookie,导致服务端拿不到会话标识。本地开发时HOST默认值为http://localhost:3000和本地访问地址一致,所以流程正常。 - 冗余逻辑风险:
checkSession函数中添加的typeof window === 'undefined'判断完全多余——该函数仅在Next.js服务端API路由中调用,运行环境永远不存在window对象,这类冗余判断在生产构建时可能被打包工具误判,导致逻辑分支异常。 - 多节点配置不一致:axios baseURL、401重定向、登录跳转三处逻辑都独立读取HOST变量拼接地址,只要一处配置错误就会触发跳转、请求异常。
分步修复
1. 修复错误处理空值判断,解决客户端崩溃
修改src/services/apiClient中的useSession逻辑,增加可选链做空值保护:
export const useSession = () => { const router = useRouter() const { isLoading, error, data, isSuccess } = useQuery<Admin, AxiosError<RedirectError>>('sid', getSession) // 逐层判断属性存在后再执行跳转 if (error?.response?.data?.redirectUrl) { router.push(error.response.data.redirectUrl) } return { isLoading, error, data, isSuccess } }
2. 改用相对路径配置axios,从根源消除跨域问题
Next.js的API路由和前端页面同域部署,不需要配置绝对路径请求地址,直接修改axios实例配置,删除HOST拼接逻辑:
// 删除原有的host常量定义,修改axios实例配置 export const apiClient = axios.create({ baseURL: "/api", // 相对路径自动匹配当前访问的协议、域名,不会触发跨域 withCredentials: true, headers: { "Content-type": "application/json" }, });
同步修改api/getSession接口中的401重定向逻辑,不要拼接HOST,直接返回相对路径:
// 删除原有的host变量定义,直接返回站内相对地址 return res.status(401).send({ redirectUrl: '/admin/login' })
3. 清理checkSession中的冗余环境判断
删除无意义的window对象判断,简化逻辑:
export default async function checkSession (token: string) { if (!token) return null const unsign = (await import('./signature')).unsign const sessionToken = unsign(token, process.env.SECRET!) if (sessionToken && typeof sessionToken === 'string') { const db = (await import('../../prisma')).default const session = await db.session.findUnique({ where: { sessionToken }, include: { admin: true } }) if (session) { return { admin: session.admin } } } return null }
4. 校正生产环境Cookie配置
找到登录接口写入sid Cookie的逻辑,按部署环境配置Cookie属性,避免浏览器拦截Cookie:
// 写入Cookie的参考配置 res.setHeader('Set-Cookie', serialize('sid', signedToken, { httpOnly: true, // 禁止前端JS读取Cookie,提升安全性 secure: process.env.NODE_ENV === 'production', // HTTPS生产环境必须开启secure sameSite: 'lax', // 同域部署使用lax即可,兼容大部分场景 path: '/', maxAge: 60 * 60 * 24 * 7 // 按实际会话有效期配置 }))
提示:如果确实存在跨域请求API的场景(Next.js同域部署无此需求),才需要将
sameSite设为none,且必须同时开启secure属性,否则浏览器不会在跨域请求中携带该Cookie。
5. 生产环境变量校验
部署前确认生产环境的SECRET等必填环境变量已正确配置,避免因为环境变量缺失导致签名校验失败,接口返回500错误。
内容的提问来源于stack exchange,提问作者XAZG Неизвестный
相关产品推荐
相关产品推荐

