Next.js中JWT认证实现:令牌存储与路由方案咨询
Hey there! 作为Next.js新手,在JWT认证这块踩坑太正常了——我当初刚上手的时候也绕了好几个弯。结合你提到的「独立API服务器返回access token」的场景,我把业界常用的标准方案拆解给你,一步步来:
存储令牌的关键是平衡安全性和渲染需求,不同方案对应不同场景:
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的渲染模式不同,认证路由的处理逻辑差异很大,得对应场景来:
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

