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

Next.js 15中实现Refresh Token Rotation的问题咨询及方案验证

Next.js 15中实现Refresh Token Rotation的问题咨询及方案验证

大家好,我正在基于Next.js 15开发应用,授权部分打算采用JWT+Refresh Token的方案,具体流程如下:

  • 当用户访问受限页面时,系统先检查是否存在access_token Cookie(有效期7分钟的JWT,包含userId):
    • 如果有效:重新生成该JWT并续期7分钟,更新Cookie,获取用户会话后继续访问
    • 如果无效:检查是否存在refresh_token Cookie(有效期7天的普通令牌):
      • 若不存在:重定向到登录页
      • 若存在:在KV/Redis数据库中验证该令牌是否有效,获取对应的userId,重新生成access_token和refresh_token,更新Cookie,获取用户会话后继续访问

我遇到的问题

卡在Refresh Token Rotation这一步上:
比如访问app/dashboard/page.js这类页面时,我能读取Cookie做验证,但没法直接更新Cookie——因为Next.js里Cookie只能通过Server Actions或者路由处理器来写入。

我试过几种方法都没解决:

  • 写了个叫UpdateAccessCookies的服务端组件放在页面里,专门用来更新Cookie,但这样会导致系统进入循环
  • 尝试用API接口,但POST请求时没法正确获取Cookie,而且我更倾向于用Server Actions来实现

给大家看部分示例代码

注册页面(app/register/page.js)

import { getCurrentSession } from '@/services/session';
import { RegisterForm } from './Form';

export default async function Register() {
  const { session, data } = await getCurrentSession();

  return (
    <>
      <h1>Register</h1>
      <RegisterForm/>
    </>
  );
}

会话处理服务(services/session.js)

'use server'

// ... 相关导入 ...

export async function getCurrentSession() {
  const accessToken = await getCookie('access_token');
  let data = {};

  if (accessToken) {
    const jwtValidation = await validarJWT(accessToken);
    if (jwtValidation) {
      data.userId = jwtValidation.userId;
      data.jwt = {
        token: await createJWT({ userId: jwtValidation.userId }),
        expiraEm: JWT_EXPIRES_IN,
      };
    }
  }

  if (!data.userId) {
    const refreshToken = await getCookie('refresh_token');
    if (!refreshToken) {
      return { session: null, data: null };
    }

    data.userId = await getStorageAdapterData('refresh_token', refreshToken);
    if (!data.userId) {
      return { session: null, data: null }; 
    }

    data.jwt = {
      token: await createJWT({ userId: data.userId }),
      expiraEm: JWT_EXPIRES_IN,
    };
    data.refreshToken = {
      token: await createRefreshToken(data.userId, refreshToken),
      expiraEm: SESSION_EXPIRES_IN,
    };
  }

  const session = await getStorageAdapterData('session', data.userId);
  if (!session) {
    return { session: null, data: null };
  }

  return { session, data };
}

我之前尝试过几个更新Cookie的位置:

  • 页面里调用getCurrentSession之后直接更新(最理想的位置,但做不到)
  • 在session.js里生成新令牌后直接更新
  • 单独做一个RefreshCookies组件放在页面里(代码如下)

旧版RefreshCookies组件(导致循环)

// Register Page 2
import { getCurrentSession } from '@/services/session';
import { RegisterForm } from './Form';
import RefreshCookies from '@/components/refresh-cookies';

export default async function Register() {
  const { session, data } = await getCurrentSession();

  return (
    <>
      <h1>Register</h1>
      <RegisterForm/>
      <RefreshCookies
        jwt={data?.jwt}
        refreshToken={data?.refreshToken}
      />
    </>
  );
}
// Refresh-cookies.js
'use client';

import { useEffect, useState } from 'react';
import { createCookie } from '@/services/cookies';

async function updateCookies(jwt, refreshToken) {
  if (jwt) {
    await createCookie('access_token', jwt.token, jwt.expires);
  }
  if (refreshToken) {
    await createCookie(
      'refresh_token',
      refreshToken.token,
      refreshToken.expires
    );
  }
}

export default function RefreshCookies({ jwt, refreshToken }) {
  const [Updatedcookies, setUpdatedCookies] = useState(false);

  useEffect(() => {
    async function update() {
      if (!Updatedcookies && (jwt || refreshToken)) {
        await updateCookies(jwt, refreshToken);
      }
    }

    update();
  }, [jwt, refreshToken, Updatedcookies]);
}

我调整后的方案(目前能运行)

我修改了RefreshCookies组件里的判断逻辑,现在代码如下:

// Refresh-cookies.js
'use client';

import { useEffect, useState } from 'react';
import { createCookie } from '@/services/cookies';

async function updateCookies(jwt, refreshToken) {
  if (jwt) {
    await createCookie('access_token', jwt.token, jwt.expires);
  }
  if (refreshToken) {
    await createCookie(
      'refresh_token',
      refreshToken.token,
      refreshToken.expires
    );
  }
}

export default function RefreshCookies({ jwt, refreshToken }) {
  const [Updatedcookies, setUpdatedCookies] = useState(false);

  useEffect(() => {
    async function update() {
      if (refreshToken || (!Updatedcookies && jwt)) { // 这里做了修改
        await updateCookies(jwt, refreshToken);
      }
    }

    update();
  }, [jwt, refreshToken, Updatedcookies]);
}

现在更新Cookie的逻辑变成:

  1. 如果收到了refreshToken(说明access_token已过期或失效),就更新Cookie
  2. 或者如果收到了jwt且还没更新过Cookie,就更新Cookie

这样就不会进入循环了,目前功能正常。想问问大家这个方案有没有什么潜在问题?或者有没有更优的实现方式?

另外补充:我不想用第三方库,也不想用中间件做验证——因为中间件会在每个受限页面请求都做检查,会显著增加请求耗时。


备注:内容来源于stack exchange,提问作者user2611485

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 19:55:31