NextJS SSR鉴权后重定向回用户原访问页面实现方案
实现方案
你之前的方案不可靠的核心原因很明确:
- 用
window.history.go(-2)依赖浏览器历史栈,用户直接地址栏输入受保护地址、跨站跳转进入等场景下,历史栈长度、来源完全不可控,大概率跳错。 - 用客户端存储(localStorage/sessionStorage/history state)记录路径的方案完全走不通:
withRequireAuth在服务端阶段就直接返回302重定向响应,不会下发页面HTML,客户端JS根本没有执行机会,不可能写入任何数据。
最稳妥的实现完全不需要依赖客户端能力,服务端就能全链路搞定,改动量极小:
核心思路
服务端拦截未认证请求的那一刻,通过请求对象req就能100%拿到用户当前要访问的准确目标路径(包含pathname和query参数),把这个路径写入临时Cookie后再跳转到认证页,等认证完成回到回调页时,从Cookie里读出之前存的目标路径,校验安全后直接跳转即可,全程不需要客户端提前执行逻辑。
具体代码改动
1. 修改withRequireAuth逻辑,拦截时写入临时Cookie
在判断需要跳转认证地址的分支里,先把目标路径写入有效期5分钟的临时Cookie,再返回重定向响应:
if (authRedirect) { // 从请求对象直接拿用户当前访问的目标路径,包含query参数 const targetPath = params.req.url ?? '/' // 写入临时Cookie,5分钟有效期足够完成认证流程 const existingCookies = params.res.getHeader('Set-Cookie') || [] params.res.setHeader('Set-Cookie', [ ...(Array.isArray(existingCookies) ? existingCookies : [existingCookies]), `auth_redirect_target=${encodeURIComponent(targetPath)}; Path=/; Max-Age=300; SameSite=Lax` ]) return { redirect: { destination: authRedirect, permanent: false, }, } }
2. 给/auth回调页加服务端逻辑,读取并清理临时Cookie
给Auth页面新增getServerSideProps,从请求Cookie里取出目标路径,做安全校验后通过props传给页面组件,同时立刻清理掉临时Cookie避免污染后续跳转:
// /auth页面新增 export async function getServerSideProps({ req, res }) { let targetPath = '/' const redirectCookie = req.cookies.auth_redirect_target if (redirectCookie) { try { const decodedPath = decodeURIComponent(redirectCookie) // 安全校验:只允许站内相对路径,避免开放重定向漏洞 if (decodedPath.startsWith('/') && !decodedPath.startsWith('//')) { targetPath = decodedPath } } catch (err) { console.error('解析认证重定向路径失败:', err) } // 清理临时Cookie res.setHeader('Set-Cookie', 'auth_redirect_target=; Path=/; Max-Age=-1') } return { props: { targetPath } } }
3. 修改Auth组件逻辑,认证完成后跳转到目标路径
接收服务端传过来的targetPath,存完认证Token后直接替换路由到目标地址即可,兜底跳首页:
// 组件入参加上targetPath export default function Auth({ targetPath = '/' }) { const router = useRouter() const { fapi_token: token, expires } = router.query const hasCredentials = token && expires useEffect(() => { if (hasCredentials) { Cookies.set(AUTH_TOKEN_KEY, token, { expires: new Date(Number(expires)), }) // 替换路由,避免用户点回退回到认证页 router.replace(targetPath) } }, [token, expires, hasCredentials, router, targetPath]) // 原有渲染逻辑保持不变 const resultComponent = hasCredentials ? ( <CircularProgress /> ) : ( <Alert severity="error"> <AlertTitle>Authentication Error</AlertTitle> Unable to authenticate due to <strong>missing MyID auth credentials</strong> </Alert> ) return <AuthResultContainer>{resultComponent}</AuthResultContainer> }
方案优势
- 100%准确:拦截时刻拿到的
req.url就是用户最初访问的地址,不管用户是点站内链接、地址栏输入、搜索引擎跳转进来的,都能正确记录,query参数也会完整保留。 - 无兼容问题:Cookie由服务端在重定向响应中写入,不需要客户端JS提前执行,完美适配SSR拦截的场景。
- 安全:加了路径校验逻辑,不会出现开放重定向漏洞。
- 改动量极小:不需要修改IdP配置、不需要调整后端FAPI逻辑,只改现有前端鉴权包装函数和Auth回调页即可。
内容的提问来源于stack exchange,提问作者Mateen Kazia
相关产品推荐
相关产品推荐

