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

Next.js 13:如何将内存中的Access Token传递给Server Component?

问题:Server Component如何使用内存中的Access Token请求登录用户专属数据?

我把Refresh Token存在Cookie中,Access Token则保存在内存的Zustand状态仓库里,客户端组件中获取Access Token的代码如下:

"use client";

import Image from "next/image";
import { motion } from "framer-motion";
import { AxiosResponse } from "axios";
import { useEffect } from "react";
import { useRouter } from "next/navigation";

import { useStore } from "@/globalState";
import { ServerResponse, userApi } from "@/types/serverResponse";
import { useGetAccessToken } from "@/customHooks/useGetAccessToken";

const UserAccess = () => {
  const updateAccessToken = useStore((state) => state.updateAccessToken);

  const router = useRouter();

  const onSuccess = (resp: AxiosResponse<ServerResponse, any>) => {
    updateAccessToken(resp.data.data);
    router.push("/");
  };

  const onError = (error: unknown) => {
    console.log("access token api call failed");
    console.log(error);
  };

  const { mutate } = useGetAccessToken({
    onSuccess,
    onError,
  });

  useEffect(() => {
    mutate();
  }, []);

  return (
    <motion.div>
......
    </motion.div>
  );
};

export default UserAccess;

现在需要在Server Component中请求仅登录用户可查看的数据,但Server Component无法通过Props传递或直接调用Zustand仓库获取内存中的Access Token,当前Server Component代码如下:

import { cookies } from "next/headers";

import NavbarContainer from "@/components/molecules/home/NavbarContainer";
import { ServerResponse, userApi } from "@/types/serverResponse";

const fetchUser = async (): Promise<ServerResponse | boolean> => {
  const refreshToken = cookies().get("refresh-token");

  if (!refreshToken) {
    return false;
  }

  const resp = await userApi.get<ServerResponse>("/user/me", {
    withCredentials: true,
    headers : {
        Authorization: // 这里需要内存中的access token
    }
  });

  // 未授权处理
  if (resp.status === 401) {
  }

  return false;
};

const Navbar = async () => {
  const user = await fetchUser();

  console.log(user);

  return <NavbarContainer />;
};

export default Navbar;

试过给Axios设置全局请求头,但只在客户端生效;每次请求都重新获取Access Token会产生大量请求;也不能把Access Token存入Cookie(有安全风险)。请问该怎么调整方案,让Server Component能使用内存中的Access Token进行数据请求?


可行方案1:在Server Component中利用Refresh Token直接获取Access Token

既然Server Component可以读取Cookie中的Refresh Token,那可以直接在服务端发起请求,用Refresh Token换取新的Access Token,再用这个Access Token请求用户数据。可以对这个流程做缓存优化,避免重复请求。

修改后的fetchUser函数示例:

const fetchUser = async (): Promise<ServerResponse | boolean> => {
  const refreshToken = cookies().get("refresh-token");

  if (!refreshToken) {
    return false;
  }

  try {
    // 第一步:用Refresh Token获取新的Access Token
    const tokenResp = await userApi.post<ServerResponse>("/token/refresh", {
      refreshToken: refreshToken.value
    }, {
      withCredentials: true
    });

    if (!tokenResp.data.data) {
      // 刷新失败,返回未登录状态
      return false;
    }

    const accessToken = tokenResp.data.data;

    // 第二步:用Access Token请求用户数据
    const userResp = await userApi.get<ServerResponse>("/user/me", {
      headers: {
        Authorization: `Bearer ${accessToken}`
      },
      withCredentials: true
    });

    if (userResp.status === 200) {
      return userResp.data;
    }
  } catch (error) {
    console.error("获取用户数据失败:", error);
    // 处理401等错误,比如清除无效的Refresh Token
    if (error.response?.status === 401) {
      // 可选:清除Cookie中的Refresh Token
      cookies().delete("refresh-token");
    }
    return false;
  }

  return false;
};

优化建议

  • 可以在服务端对获取到的Access Token做短期内存缓存(比如用Redis或者内存缓存库),键用Refresh Token的哈希值,有效期设为Access Token的过期时间,避免每次Server Component渲染都重复请求Token接口。

可行方案2:将需要用户数据的逻辑移到客户端组件

如果Server Component不需要在服务端预渲染用户数据,可以把请求用户数据的部分改成客户端组件,直接从Zustand仓库读取Access Token发起请求,再将数据传递给子组件(包括Server Component)。

示例:

// 客户端组件:UserInfoClient.tsx
"use client";

import { useEffect, useState } from "react";
import { useStore } from "@/globalState";
import { ServerResponse, userApi } from "@/types/serverResponse";
import Navbar from "./Navbar"; // 原Server Component

const UserInfoClient = () => {
  const accessToken = useStore((state) => state.accessToken);
  const [userData, setUserData] = useState<ServerResponse | null>(null);

  useEffect(() => {
    const fetchUser = async () => {
      if (!accessToken) return;
      try {
        const resp = await userApi.get<ServerResponse>("/user/me", {
          headers: {
            Authorization: `Bearer ${accessToken}`
          }
        });
        setUserData(resp.data);
      } catch (error) {
        console.error("获取用户数据失败:", error);
      }
    };

    fetchUser();
  }, [accessToken]);

  return <Navbar userData={userData} />;
};

export default UserInfoClient;

然后修改原Server Component接收props:

// 原Server Component Navbar.tsx
import NavbarContainer from "@/components/molecules/home/NavbarContainer";
import { ServerResponse } from "@/types/serverResponse";

interface NavbarProps {
  userData: ServerResponse | null;
}

const Navbar = ({ userData }: NavbarProps) => {
  console.log(userData);
  return <NavbarContainer userData={userData} />;
};

export default Navbar;

方案3:使用Next.js的Server Actions处理数据请求

可以把请求用户数据的逻辑封装成Server Action,在客户端组件中调用这个Action,Server Action可以读取Cookie中的Refresh Token,自动获取Access Token并请求数据,同时Server Action的结果可以传递给Server Component或者客户端组件使用。

示例:

// app/actions.ts
"use server";

import { cookies } from "next/headers";
import { ServerResponse, userApi } from "@/types/serverResponse";

export async function fetchUserAction(): Promise<ServerResponse | boolean> {
  const refreshToken = cookies().get("refresh-token");

  if (!refreshToken) {
    return false;
  }

  try {
    // 先刷新获取Access Token
    const tokenResp = await userApi.post<ServerResponse>("/token/refresh", {
      refreshToken: refreshToken.value
    }, { withCredentials: true });

    if (!tokenResp.data.data) return false;

    const accessToken = tokenResp.data.data;
    const userResp = await userApi.get<ServerResponse>("/user/me", {
      headers: { Authorization: `Bearer ${accessToken}` },
      withCredentials: true
    });

    return userResp.data;
  } catch (error) {
    console.error("Server Action获取用户数据失败:", error);
    if (error.response?.status === 401) {
      cookies().delete("refresh-token");
    }
    return false;
  }
}

然后在客户端组件中调用这个Action:

"use client";

import { useActionState } from "react";
import { fetchUserAction } from "@/app/actions";
import Navbar from "./Navbar";

const UserAccess = () => {
  const [userData, formAction] = useActionState(fetchUserAction, null);

  // 组件挂载时触发Action
  useEffect(() => {
    formAction();
  }, [formAction]);

  return <Navbar userData={userData} />;
};

export default UserAccess;

内容的提问来源于stack exchange,提问作者Diego Davincci

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 19:23:14