You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 15:45:32