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

Next.js 14服务端组件中如何正确存储JWT Token以访问API?

解决方案

核心思路是把JWT Token存储在HttpOnly Cookie中,这样服务端渲染时可以直接从请求头的Cookie里读取Token,无需依赖前端localStorage,也不用把组件改成客户端组件。

1. 后端调整(NestJS)

登录接口返回响应时,将JWT设置为HttpOnly Cookie,而非返回给前端自行存储:

@Post('login')
async login(
  @Body() loginDto: LoginDto,
  @Res({ passthrough: true }) res: Response
) {
  // 验证用户身份并生成JWT
  const user = await this.authService.validateUser(loginDto);
  const token = this.authService.generateJwt(user);

  // 设置Cookie(生产环境需开启Secure属性)
  res.cookie('access_token', token, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'strict',
    maxAge: 3600000 // 1小时有效期
  });

  return { message: '登录成功' };
}

2. 前端Server Component修改(Next.js App Router)

利用Next.js提供的headers()函数在服务端读取Cookie,提取Token后放入API请求头:

import { headers } from 'next/headers';
import Link from 'next/link';

async function getData() {
  const headersList = headers();
  const cookie = headersList.get('cookie');
  
  // 从Cookie中解析出Token
  let token = '';
  if (cookie) {
    const tokenPair = cookie.split('; ').find(pair => pair.startsWith('access_token='));
    token = tokenPair?.split('=')[1] || '';
  }

  if (!token) throw new Error('未获取到登录凭证');

  const res = await fetch('apiurl', {
    cache: 'no-store',
    headers: {
      'Authorization': `Bearer ${token}`
    }
  });

  if (!res.ok) throw new Error('获取用户列表失败');
  return res.json();
}

const UsersPage = async () => {
  const users = await getData();

  return (
    <div>
      <ol>
        {users?.map(user => (
          <li key={user.id}> {/* 用用户ID作为key比索引更稳定 */}
            <Link href={`/users/${user.id}`}>{user.businessName}</Link>
          </li>
        ))}
      </ol>
    </div>
  );
};

export default UsersPage;

3. 前端Pages Router SSR版本

如果使用的是Pages Router,通过getServerSideProps在服务端获取Cookie并传递给组件:

import { GetServerSideProps } from 'next';
import Link from 'next/link';

async function getData(token: string) {
  const res = await fetch('apiurl', {
    cache: 'no-store',
    headers: {
      'Authorization': `Bearer ${token}`
    }
  });

  if (!res.ok) throw new Error('获取用户列表失败');
  return res.json();
}

const UsersPage = ({ users }) => {
  return (
    <div>
      <ol>
        {users?.map(user => (
          <li key={user.id}>
            <Link href={`/users/${user.id}`}>{user.businessName}</Link>
          </li>
        ))}
      </ol>
    </div>
  );
};

export const getServerSideProps: GetServerSideProps = async (context) => {
  const cookie = context.req.headers.cookie;
  let token = '';

  if (cookie) {
    const tokenPair = cookie.split('; ').find(pair => pair.startsWith('access_token='));
    token = tokenPair?.split('=')[1] || '';
  }

  // 无Token则跳转到登录页
  if (!token) {
    return { redirect: { destination: '/login', permanent: false } };
  }

  try {
    const users = await getData(token);
    return { props: { users } };
  } catch (err) {
    return { redirect: { destination: '/login', permanent: false } };
  }
};

export default UsersPage;

关键说明

  • HttpOnly Cookie无法被前端JS读取,避免了XSS攻击风险,同时服务端能直接从请求头获取,完美适配SSR场景。
  • 生产环境务必开启secure属性,确保Cookie仅通过HTTPS传输。
  • 若Token过期,可在后端返回401状态码,前端捕获后自动跳转到登录页。

内容的提问来源于stack exchange,提问作者Ghias Ali

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 02:14:53