NextJS外部链接访问时显示已登出问题排查
网站右上角的用户栏组件,登录时显示用户名,未登录显示「Sign In」链接。但通过外部网站链接访问时,页面显示「Sign In」,网站判定未登录;刷新页面后又显示已登录状态(此时nxtCookies.getAll()为空);直接输入URL访问时登录状态显示正常。
UserInfoBar 组件代码
import React from 'react' import Link from 'next/link' import { getSession } from '../../lib/session' export default async function UserInfoBar() { let sessionData = await getSession(); return ( { Object.keys(sessionData).length > 0 ? {sessionData.username}: <Link href='/signin'>Sign In</Link> } ) }
session 工具代码
import { cookies } from 'next/headers'; import prisma from './prisma' import jwt from 'jsonwebtoken' export const getSession = async () => { const nxtCookies = cookies(); if (nxtCookies.has('wp_session')) { let sessionData = jwt.verify(nxtCookies.get('wp_session').value, process.env.ACCESS_TOKEN_SECRET, async (err, user) => { if (!err) { let r = await prisma.user.findUnique({ where: { id: user.id } }); if (r) { return { id: r?.id, email: r?.email, username: r?.username }; } } return false; }); if (sessionData) return sessionData; } return false; }
登录时设置Cookie的代码
setCookie('wp_session', token, { path: '/', maxAge: 3600 * 24 * 7 * 30, sameSite: true });
1. SameSite Cookie配置错误
设置Cookie时sameSite: true不符合规范,Next.js的setCookie方法中,sameSite的合法值为字符串:'strict'、'lax'、'none',布尔值会被浏览器判定为无效配置。
从外部网站跳转属于跨站请求,若SameSite配置不正确,浏览器不会携带Cookie到服务端,导致getSession拿不到wp_session,显示未登录;而刷新页面是同站请求,浏览器会携带Cookie,因此能正确识别登录状态。
修复方案:
根据业务需求调整SameSite值:
- 若允许跨站GET请求携带Cookie,设置为
'lax'(默认推荐值) - 若需支持第三方嵌入场景,设置为
'none'(需同时开启secure: true,仅HTTPS环境生效)
setCookie('wp_session', token, { path: '/', maxAge: 3600 * 24 * 7 * 30, sameSite: 'lax', // 若使用sameSite: 'none',需添加以下配置 // secure: process.env.NODE_ENV === 'production' });
2. jwt.verify异步回调使用错误
jwt.verify的回调是异步执行的,但你直接将jwt.verify的返回值赋值给sessionData,而该方法在使用回调时返回的是undefined,无法获取异步验证后的用户数据,导致sessionData始终为undefined,最终返回false。
修复方案:
改用同步方式验证JWT,避免异步回调导致的取值错误:
export const getSession = async () => { const nxtCookies = cookies(); if (nxtCookies.has('wp_session')) { try { // 同步验证JWT const user = jwt.verify(nxtCookies.get('wp_session').value, process.env.ACCESS_TOKEN_SECRET); // 查询用户信息 const userInfo = await prisma.user.findUnique({ where: { id: user.id } }); if (userInfo) { return { id: userInfo.id, email: userInfo.email, username: userInfo.username }; } } catch (err) { // 处理JWT验证失败、用户不存在等异常 return false; } } return false; }
3. 服务端组件与客户端状态同步问题
UserInfoBar作为服务端组件,首次从外部链接访问时,服务端因Cookie未携带而渲染「Sign In」;刷新时服务端能拿到Cookie,渲染用户名。若需要客户端也能实时获取登录状态,可结合document.cookie读取客户端Cookie,或使用SWR/React Query做状态同步,但优先解决前两个核心问题即可覆盖大部分场景。
内容的提问来源于stack exchange,提问作者X33

