Next.js中如何持久化存储客户端JWT 重启浏览器/设备不丢失
问题原因
你当前使用的useState是组件内存级状态,仅在当前页面的组件生命周期内生效,页面刷新、浏览器关闭后内存被回收,存储的值自然会被清空,完全不具备持久化能力。另外你原来写的useState(String)本身写法有误,传入的是String构造函数而非具体的初始值,正常初始化应该传入对应类型的初始值,比如空字符串''。
实现方案
要实现浏览器重启、设备重启后仍可读取的持久化存储,直接用浏览器原生的localStorage即可,它是持久化存储,除非用户手动清除浏览器缓存数据,否则写入的数据会永久保留,完全满足你24小时JWT留存的需求。
注意Next.js默认带服务端渲染(SSR/SSG),服务端环境下没有window对象,直接访问localStorage会抛错,需要做客户端环境判断。
具体实现步骤
- 组件初始化时,优先从
localStorage读取之前存储的JWT作为storedVal的初始值 - 每次更新
storedVal时,同步把值写入localStorage - 额外增加JWT过期校验逻辑:存储时同时记录JWT的生成时间,读取时先判断是否超出24小时有效期,过期就直接清除旧值,触发重新生成流程,避免使用无效token
改造后的代码示例
import { useState, useEffect } from 'react' // 项目里其他依赖比如VStack、Heading、Button、connect相关逻辑保持原有导入不变 const JWT_STORAGE_KEY = 'lit_protocol_jwt' const JWT_MAX_AGE = 24 * 60 * 60 * 1000 // 24小时有效期,单位毫秒 export default function Home(props: any) { const [storedVal, setStoredVal] = useState<string>('') const [connected, setConnected] = useState(false) // 原有connected状态逻辑保持不变 // 组件挂载到客户端后,从localStorage读取初始值 useEffect(() => { const storedData = localStorage.getItem(JWT_STORAGE_KEY) if (!storedData) return try { const { value, expireAt } = JSON.parse(storedData) // 未过期才赋值,过期直接清除旧数据 if (Date.now() < expireAt) { setStoredVal(value) } else { localStorage.removeItem(JWT_STORAGE_KEY) } } catch (e) { // 数据格式异常直接清空 localStorage.removeItem(JWT_STORAGE_KEY) } }, []) function storePersistence(jwtValue: string) { setStoredVal(jwtValue) // 写入localStorage的同时记录过期时间 const storageData = { value: jwtValue, expireAt: Date.now() + JWT_MAX_AGE } localStorage.setItem(JWT_STORAGE_KEY, JSON.stringify(storageData)) } function printPersistence() { console.log('当前存储的JWT:', storedVal) } // 原有connect逻辑保持不变 const connect = () => { setConnected(true) // 登录/注册流程拿到第三方返回的新JWT后,直接调用storePersistence传入JWT即可 // 示例:storePersistence(接口返回的jwt字符串) } return ( <> <VStack> <Heading as='h3'>Connect to see unity app</Heading> { !connected ? <Button variant='solid' onClick={connect}>Connect</Button> : <Text>Now you can click on protected path link at the bottom</Text> }<br></br> <Button variant='solid' onClick={() => storePersistence('I set this')}>Store Persistence</Button> <Button variant='solid' onClick={printPersistence}>Check Persistence</Button> </VStack> </> ) }
注意事项
- 不要用
sessionStorage替代,它的生命周期仅在当前标签页生效,标签页关闭后数据就会被清除,不符合你的需求 - 如果需要存储更大量的数据、或者需要更强的持久化能力,可以替换为IndexedDB存储,但对于单条JWT字符串的场景,
localStorage是最轻量、实现成本最低的方案 - 该方案在Next.js的Pages Router、App Router客户端组件下均可正常运行,不会触发服务端渲染报错
内容的提问来源于stack exchange,提问作者NextNightFlyer
相关产品推荐
相关产品推荐

