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

Next.js中JWT认证实现:令牌存储与路由方案咨询

Hey there! 作为Next.js新手,在JWT认证这块踩坑太正常了——我当初刚上手的时候也绕了好几个弯。结合你提到的「独立API服务器返回access token」的场景,我把业界常用的标准方案拆解给你,一步步来:

一、JWT令牌的存储方案(核心是安全+适配Next.js场景)

存储令牌的关键是平衡安全性和渲染需求,不同方案对应不同场景:

  • HttpOnly Cookie(最推荐,适配SSR/CSR混合场景)
    这是生产环境最常用的方案,安全系数最高。登录成功后,让你的API服务器把access token(以及可选的refresh token)设置成HttpOnly、Secure、SameSite=Strict的Cookie。
    举个API响应头的例子:

    Set-Cookie: access_token=xxx; HttpOnly; Secure; SameSite=Strict; Path=/
    

    好处是:令牌不会被前端JS读取,能有效防御XSS攻击;Next.js不管是服务端渲染还是客户端请求API,浏览器都会自动带上Cookie,不用手动处理令牌传递。
    注意:如果Next.js和API服务器跨域,要在API的CORS配置里允许携带凭证(credentials: true),同时Next.js的fetch或axios请求要加credentials: 'include'。

  • LocalStorage(仅适合纯CSR场景,谨慎使用)
    如果你的Next.js应用完全是客户端渲染(没有SSR需求),也可以把access token存在LocalStorage里,但要警惕XSS风险——一旦页面被注入恶意脚本,令牌很容易被窃取。使用时需要每次请求API手动在请求头里加:

    headers: { Authorization: `Bearer ${localStorage.getItem('access_token')}` }
    

    不推荐在有SSR需求的场景用,因为LocalStorage在服务器端无法读取,会导致渲染时认证状态不一致。

  • React Context/State(临时状态,配合持久化方案)
    可以把用户登录状态(比如用户名、是否登录)存在客户端的Context里,方便组件间共享,但刷新页面后会丢失,所以必须结合Cookie或LocalStorage做持久化。比如登录成功后,先把token存在Cookie,再把API返回的用户信息存在Context里,快速标识登录状态。

二、Next.js的认证路由处理(分渲染场景对应方案)

Next.js的渲染模式不同,认证路由的处理逻辑差异很大,得对应场景来:

1. 服务端渲染(SSR):用getServerSideProps做前置验证

在需要保护的页面,通过getServerSideProps在服务器端完成令牌验证,未认证就直接重定向到登录页,避免泄露敏感内容。
示例代码:

export async function getServerSideProps(context) {
  // 从请求头的Cookie中读取access token
  const accessToken = context.req.cookies.access_token;

  if (!accessToken) {
    return {
      redirect: { destination: '/login', permanent: false }
    };
  }

  // 调用API验证令牌有效性
  try {
    const res = await fetch('https://your-api-domain.com/api/verify-token', {
      headers: { Authorization: `Bearer ${accessToken}` }
    });

    if (!res.ok) throw new Error('Token invalid');
    const user = await res.json();
    return { props: { user } };
  } catch (err) {
    return { redirect: { destination: '/login', permanent: false } };
  }
}

function ProtectedSSRPage({ user }) {
  return <div>欢迎回来,{user.username}!</div>;
}

export default ProtectedSSRPage;

2. 静态生成(SSG):客户端验证+状态管理

SSG页面是预渲染的,服务器端拿不到实时认证状态,所以要在客户端完成验证。可以用useEffect或者SWR/React Query这类数据缓存库来处理:
示例(用SWR简化逻辑):

import useSWR from 'swr';
import { useRouter } from 'next/router';

// 封装请求逻辑,自动携带Cookie
const fetcher = async (url) => {
  const res = await fetch(url, { credentials: 'include' });
  if (!res.ok) throw new Error('未授权');
  return res.json();
};

function ProtectedSSGPage() {
  const router = useRouter();
  const { data: user, error } = useSWR('/api/me', fetcher);

  // 加载中状态
  if (!user && !error) return <div>加载中...</div>;

  // 未认证则重定向
  if (error) {
    router.push('/login');
    return null;
  }

  return <div>欢迎回来,{user.username}!</div>;
}

export default ProtectedSSGPage;

3. 简化方案:用NextAuth.js一站式处理

如果不想自己写复杂的认证逻辑,NextAuth.js是官方推荐的工具,支持JWT、OAuth等多种认证方式,能帮你搞定路由守卫、令牌刷新、状态管理等所有细节。
示例配置JWT认证:

// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';

export default NextAuth({
  providers: [
    CredentialsProvider({
      async authorize(credentials) {
        // 调用你的独立API验证用户名密码
        const res = await fetch('https://your-api-domain.com/api/login', {
          method: 'POST',
          body: JSON.stringify(credentials),
          headers: { 'Content-Type': 'application/json' }
        });
        const user = await res.json();

        if (res.ok && user) {
          // 返回用户信息和API返回的access token
          return { ...user, accessToken: user.access_token };
        }
        return null;
      }
    })
  ],
  session: { strategy: 'jwt' },
  callbacks: {
    async jwt({ token, user }) {
      // 登录时把API的access token存入NextAuth的JWT中
      if (user) token.accessToken = user.accessToken;
      return token;
    },
    async session({ session, token }) {
      // 把access token传给客户端session
      session.user.accessToken = token.accessToken;
      return session;
    }
  }
});

然后在保护页面用useSession钩子:

import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';

function ProtectedPage() {
  const { data: session, status } = useSession();
  const router = useRouter();

  if (status === 'loading') return <div>加载中...</div>;
  if (!session) {
    router.push('/login');
    return null;
  }

  return (
    <div>
      欢迎回来,{session.user.username}!
      <button onClick={() => signOut()}>退出登录</button>
    </div>
  );
}

export default ProtectedPage;
三、避坑小提示
  • 令牌刷新:access token过期后,用存在HttpOnly Cookie里的refresh token调用API刷新,Next.js里可以用SWR的自动重试机制,或者在请求拦截器里统一处理。
  • CSRF防护:如果用Cookie存令牌,一定要开启CSRF保护,API服务器要验证CSRF令牌(比如Next.js的API路由可以用csrf库)。
  • 环境变量:API地址等敏感信息存在.env.local里,客户端需要访问的变量要加NEXT_PUBLIC_前缀。

内容的提问来源于stack exchange,提问作者Kongpon Charanwattanakit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:03:43