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

Next.js前端如何正确存储敏感数据防止用户篡改

Next.js角色权限类全局数据实现方案

先澄清两个常见的实现误区

  • Redux在Next.js中出现渲染丢数据的问题,本质是实现方式错误:没有做客户端单例store封装、没有做服务端状态到客户端的注水流程,每次路由切换/重渲染都重新初始化空store,才会导致状态丢失,并非Redux本身不适用Next.js场景
  • SWR不存在“只能在发起请求的组件内读取缓存”的限制:只要在应用顶层统一配置SWRConfig,全局所有组件调用相同key的useSWR都会共享同一份内存缓存,跨组件、跨路由读取都不会有问题,之前读取失败是因为没有做全局配置,不同组件树的SWR实例用了独立的缓存空间

前端所有权限控制仅做UI层拦截,所有敏感操作、数据查询的权限校验必须在后端完成。前端没有绝对的防篡改能力,方案设计的核心目标是提升篡改成本、避免明文存储敏感数据、保证全局状态的一致性。

优先推荐方案:基于SWR全局缓存+自定义Hook(适配现有技术栈,改造成本最低)

该方案完全匹配“像useContext一样任意组件引入复用数据”的诉求,不需要额外引入重型状态库,基于已经在使用的SWR即可实现:

  1. 在应用根组件(App Router对应app/layout.tsx,Pages Router对应pages/_app.tsx)顶层包裹全局SWRConfig,配置统一的fetcher、缓存规则,保证全局SWR实例共享同一份缓存空间
  2. 登录成功后,使用固定全局key(例如/api/user/auth、/api/user/accessible-products)发起SWR请求拉取用户角色、权限、可访问产品列表数据,配置revalidateIfStale: false、revalidateOnFocus: false减少不必要的重复请求,数据默认存储在SWR内存缓存中,不写入本地存储
  3. 封装通用自定义Hook,替代原有的useContext逻辑:
    • 封装useAuth()Hook,内部调用useSWR('/api/user/auth'),返回用户信息、角色、权限列表、加载状态、权限判断工具字段
    • 封装useUserProducts()Hook,内部调用useSWR('/api/user/accessible-products'),返回用户可访问的产品列表、加载状态
  4. 任意组件、任意路由下直接引入上述Hook即可获取全局共享的最新数据,不需要嵌套多层Context Provider,使用方式比useContext更简洁

核心代码示例

根组件全局SWR配置:

// app/layout.tsx
import { SWRConfig } from 'swr'

const globalFetcher = async (url: string) => {
  const res = await fetch(url, { credentials: 'include' })
  if (!res.ok) throw new Error('请求失败')
  return res.json()
}

export default function RootLayout({ children }) {
  return (
    <html lang="zh-CN">
      <body>
        <SWRConfig
          value={{
            fetcher: globalFetcher,
            revalidateIfStale: false,
            revalidateOnFocus: false,
            shouldRetryOnError: false
          }}
        >
          {children}
        </SWRConfig>
      </body>
    </html>
  )
}

权限Hook封装:

// hooks/useAuth.ts
import useSWR from 'swr'

export function useAuth() {
  const { data, error, isLoading } = useSWR(
    '/api/user/auth',
    // 未登录时不发起请求
    { isPaused: () => !document.cookie.includes('access_token') }
  )

  return {
    user: data?.userInfo,
    role: data?.role, // admin / user
    permissions: data?.permissions || [],
    isLoading,
    isAuthError: !!error,
    isAdmin: data?.role === 'admin'
  }
}

组件内使用示例:

// 任意业务组件
import { useAuth } from '@/hooks/useAuth'

export default function ProductPage() {
  const { isAdmin, permissions } = useAuth()
  // 直接使用权限数据做UI判断
  return isAdmin ? <AdminProductPanel /> : <UserProductPanel />
}

备选方案:Redux正确实现方式(适合复杂状态场景)

如果后续业务状态逻辑复杂度提升,需要用Redux承载更多全局状态,只要做对以下配置就不会出现丢数据问题:

  • 客户端Redux store做单例封装,避免每次重渲染都新建空store
  • 配合RTK Query实现接口数据拉取、缓存能力,权限、产品类数据直接用RTK Query生成的Hook全局调用,自动共享缓存
  • 敏感状态仅存储在内存中,不做localStorage持久化,服务端渲染场景下做好注水流程,把服务端拿到的初始状态同步到客户端store

必做安全兜底规则

  • 角色、权限列表、可访问产品范围这类敏感数据,仅存在前端运行时内存中,禁止明文存储在localStorage、sessionStorage等可被用户手动修改的本地存储介质中,页面刷新后通过合法登录态重新向后端拉取最新数据
  • 所有后端接口必须独立做权限校验,禁止信任前端请求中携带的角色、权限字段,前端的权限判断仅用于控制UI元素展示、路由跳转引导,不构成实际的安全边界
  • 若需要进一步提升篡改成本,可对接口返回的权限数据做签名校验,前端验签发现数据被篡改时,直接清空登录态跳转至登录页

内容的提问来源于stack exchange,提问作者RAFAEL DA SILVA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 18:45:32