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

Next.js 13 App目录服务端组件401错误下的Token刷新处理方案

服务端组件中401错误的处理方案

首先明确:不是必须被迫使用客户端组件,服务端场景下有几种针对性的处理思路,可根据业务场景选择:

1. 服务端直接重定向到登录页

如果你的应用依赖会话式身份验证(比如用HTTP-only cookie存储refresh token),服务端组件请求API遇到401时,可直接发起重定向跳转至登录页。这种方式适用于refresh token也已过期、需要用户重新验证身份的场景。

示例代码(Next.js服务端组件):

async function ServerComponent() {
  try {
    const accessToken = cookies().get('accessToken')?.value;
    const response = await fetch('https://your-api.com/protected-resource', {
      headers: { Authorization: `Bearer ${accessToken}` },
      cache: 'no-store'
    });

    if (!response.ok) {
      if (response.status === 401) {
        // 服务端触发重定向
        redirect('/login');
      }
      throw new Error('资源请求失败');
    }

    const data = await response.json();
    return <div>{data.content}</div>;
  } catch (err) {
    return <div>加载失败,请稍后重试</div>;
  }
}

2. 服务端中间件统一处理Token刷新

将401拦截和token刷新逻辑放在服务端中间件(或API路由)中,而非服务端组件内。这种方式能让组件专注于渲染逻辑,权限处理逻辑全局统一。

示例代码(Next.js中间件):

export async function middleware(request: NextRequest) {
  const accessToken = request.cookies.get('accessToken')?.value;
  const refreshToken = request.cookies.get('refreshToken')?.value;

  if (!accessToken) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  // 校验当前accessToken有效性
  const authCheckResponse = await fetch('https://your-api.com/check-auth', {
    headers: { Authorization: `Bearer ${accessToken}` }
  });

  if (authCheckResponse.status === 401 && refreshToken) {
    // 调用刷新token接口
    const refreshResponse = await fetch('https://your-api.com/refresh-token', {
      method: 'POST',
      body: JSON.stringify({ refreshToken })
    });

    if (refreshResponse.ok) {
      const { newAccessToken } = await refreshResponse.json();
      // 更新cookie中的accessToken
      const newResponse = NextResponse.next();
      newResponse.cookies.set('accessToken', newAccessToken, { 
        httpOnly: true, 
        secure: process.env.NODE_ENV === 'production' 
      });
      return newResponse;
    } else {
      // refreshToken失效,强制跳转登录
      return NextResponse.redirect(new URL('/login', request.url));
    }
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/protected/:path*']
};

3. 局部降级为客户端组件(特定场景)

如果业务需要保留当前页面状态、不能直接跳转到登录页,可将需要处理401的动态内容抽离为客户端组件,服务端组件负责渲染静态、无需权限校验的部分。

示例代码:

// 父级服务端组件
async function ParentServerComponent() {
  // 渲染静态公共内容
  return (
    <div>
      <h1>用户中心</h1>
      {/* 权限相关内容交由客户端组件处理 */}
      <UserProfile />
    </div>
  );
}

// 客户端组件 'use client'
'use client';
import { useEffect, useState } from 'react';

function UserProfile() {
  const [profile, setProfile] = useState(null);
  const [error, setError] = useState('');

  useEffect(() => {
    async function fetchProfile() {
      let accessToken = localStorage.getItem('accessToken');
      let response = await fetch('https://your-api.com/user-profile', {
        headers: { Authorization: `Bearer ${accessToken}` }
      });

      if (response.status === 401) {
        const refreshToken = localStorage.getItem('refreshToken');
        const refreshRes = await fetch('https://your-api.com/refresh-token', {
          method: 'POST',
          body: JSON.stringify({ refreshToken })
        });

        if (refreshRes.ok) {
          const { newAccessToken } = await refreshRes.json();
          localStorage.setItem('accessToken', newAccessToken);
          // 重新发起请求
          response = await fetch('https://your-api.com/user-profile', {
            headers: { Authorization: `Bearer ${newAccessToken}` }
          });
        } else {
          setError('登录已过期,请重新登录');
          window.location.href = '/login';
          return;
        }
      }

      const data = await response.json();
      setProfile(data);
    }

    fetchProfile();
  }, []);

  if (error) return <div className="error">{error}</div>;
  if (!profile) return <div>加载中...</div>;
  return <div>用户名:{profile.name}</div>;
}

总结

服务端组件遇到401时,优先选择服务端重定向或中间件统一处理,这两种方式更贴合服务端渲染的特性,避免权限逻辑分散。只有在需要保留页面状态、必须前端处理的场景下,再考虑局部使用客户端组件。

内容的提问来源于stack exchange,提问作者Chanwoo Park

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 17:08:18