news 2026/9/19 20:35:37

Front-End-Checklist 实战:按 GDPR 第 17 条实现用户数据删除机制(Right to Erasure)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Front-End-Checklist 实战:按 GDPR 第 17 条实现用户数据删除机制(Right to Erasure)

Front-End-Checklist 实战:按 GDPR 第 17 条实现用户数据删除机制(Right to Erasure)

【免费下载链接】Front-End-Checklist🗂 The essential checklist for modern web development, for humans and AI agents项目地址: https://gitcode.com/gh_mirrors/fr/Front-End-Checklist

本文以 Front-End-Checklist 仓库中的 right-to-erasure 规则文档(references/rule.md 与 SKILL.md)为主体,系统讲解面向用户的数据删除机制应如何设计:从账户设置里的删除入口、两步确认交互,到localStorage/sessionStorage/IndexedDB/Cookie/Service Worker 缓存的全面清理,再到服务端删除 API 与 30 天期限的落地。读完本文,你将能依据 GDPR Article 17 在自己的前端应用中实现一套完整、可审计、可通过验证清单的自助删除流程。

一、什么是"被遗忘权":GDPR 第 17 条为什么关乎前端

被遗忘权(Right to erasure / Right to be forgotten)是 GDPR(《通用数据保护条例》)第 17 条赋予欧盟境内用户的核心权利:当个人数据不再为收集目的所必需,或用户撤回同意时,数据控制者(controller)必须应请求删除其个人数据,除非存在继续处理的法定理由。

对前端应用而言,这一义务天然地分为两个维度:

  1. 清除全部客户端存储——浏览器里散落的localStoragesessionStorage、IndexedDB、Cookie、Service Worker 缓存;
  2. 触发服务端删除——向删除 API 发起请求,让服务器在 30 天内完成数据清除。

原文档特别强调了一个合规动机:不履行这一权利可能招致最高2000 万欧元或全球年营业额 4%的监管罚款。与此同时,一个清晰、可发现的删除流程本身就是建立用户信任的关键体验,属于隐私质量检查清单中的核心项。

在仓库中,这条规则被编排在 packages/content/rules/en/privacy/right-to-erasure.mdx,归属于privacy分类下的data-rights子类,优先级 medium、难度 intermediate、预估耗时 60 分钟;同时它也是 Privacy & Consent 检查清单 的组成规则之一,并在 README.md 的整体清单中作为可勾选项出现。

二、入口设计与两步确认:删除选项必须可被发现

删除机制的第一步是"用户能找到它"。原文档给出的设计原则是:把删除入口放在账户设置(Account Settings)中,并置于诸如 "Privacy" 或 "Your data" 这样语义清晰的分区之下;同时,为避免误触导致数据被意外清除,必须采用两步确认模式——用户先点击入口进入确认态,再显式点击确认按钮才真正执行。

// DeletionRequestButton.tsx import { useState } from 'react'; import { clearUserData } from '@/lib/privacy'; type DeletionState = 'idle' | 'confirming' | 'pending' | 'done' | 'error'; export function DeletionRequestButton() { const [state, setState] = useState<DeletionState>('idle'); async function handleConfirm() { setState('pending'); try { // 1. Clear all client-side storage immediately clearUserData(); // 2. Send deletion request to the server const response = await fetch('/api/account/delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, }); if (!response.ok) throw new Error('Server deletion request failed'); setState('done'); } catch { setState('error'); } } if (state === 'done') { return ( <p role="status"> Your deletion request has been received. Your data will be removed within 30 days. You have been signed out. </p> ); } if (state === 'confirming') { return ( <div role="alertdialog" aria-labelledby="delete-confirm-title"> <h2 id="delete-confirm-title">Delete your account and all data?</h2> <p>This action cannot be undone. You will be signed out immediately.</p> <button onClick={handleConfirm} disabled={state === 'pending'}> {state === 'pending' ? 'Deleting…' : 'Yes, delete my data'} </button> <button onClick={() => setState('idle')}>Cancel</button> </div> ); } return ( <button onClick={() => setState('confirming')}> Delete my account and data </button> ); }

这个组件用状态机(idle → confirming → pending → done / error)管理整个删除流程,有三处值得注意的细节:

  • 确认对话框使用role="alertdialog"aria-labelledby:这不是普通的弹窗,而是语义化的模态告警,屏幕阅读器用户能明确感知"这是需要决断的删除操作";
  • 完成态使用role="status":删除请求受理后,辅助技术可以播报这一状态变化;
  • pending状态禁用按钮:防止重复提交,同时按钮文案切换为 "Deleting…" 给予即时反馈。

从源码结构看,原文档在 right-to-erasure.mdx 中为该示例补全了import与组件导出,删除了引用文档中的残缺片段,使其成为可直接编译运行的完整组件。

三、必须删除什么:数据存放位置的完整清单

一次合规的删除必须覆盖所有可能存放个人数据的位置。原文档给出了一张"存放位置 → 清理 API"的对照表,这是实施时逐项核对的地图:

存放位置清理 API
localStoragelocalStorage.clear()或按 key 调用removeItem
sessionStoragesessionStorage.clear()
IndexedDBindexedDB.deleteDatabase(name)
Cookies将每个 Cookie 设置为Max-Age=0; expires=Thu, 01 Jan 1970
Service Worker 缓存caches.delete(cacheName)
服务端数据向删除 API 发起 DELETE / POST 请求

值得注意的是,这张表把"服务端数据"也并列纳入——客户端清理只是删除机制的一半,任何只做了前端清理、却把个人数据留在服务器上的实现,都不能算作满足 GDPR 第 17 条。

四、clearUserData():把客户端清理收敛为一个可审计函数

原文档强烈建议把客户端所有存储机制的清理逻辑集中到一个clearUserData()函数中。集中化的收益是可审计、可扩展:审查者只需查看这一个函数,就能确认所有存储位置都被覆盖,未来新增存储类型时也只需在此扩展。

// lib/privacy.ts /** * Removes all personal data from client-side storage. * Call this immediately when a deletion request is confirmed — do not wait * for the server response, as the user has already expressed intent to delete. */ export async function clearUserData(): Promise<void> { // 1. localStorage localStorage.clear(); // 2. sessionStorage sessionStorage.clear(); // 3. IndexedDB — delete every database the application has created const databases = await indexedDB.databases(); await Promise.all( databases.map((db) => { if (!db.name) return Promise.resolve(); return new Promise<void>((resolve, reject) => { const req = indexedDB.deleteDatabase(db.name!); req.onsuccess = () => resolve(); req.onerror = () => reject(req.error); }); }) ); // 4. Cookies — clear known application cookies const cookiesToDelete = ['session', 'refresh_token', 'user_prefs', '__stripe_mid']; for (const name of cookiesToDelete) { document.cookie = `${name}=; Max-Age=0; path=/; SameSite=Lax`; } // 5. Cache Storage (Service Worker caches) if ('caches' in window) { const cacheNames = await caches.keys(); await Promise.all(cacheNames.map((name) => caches.delete(name))); } }

几个需要展开的实现细节:

  • IndexedDB 的删除是异步且逐个进行的:先通过indexedDB.databases()枚举所有数据库(注意该 API 可能返回空name的占位项,需要过滤),再用Promise包装deleteDatabase的成功/失败回调,最后以Promise.all并行等待全部完成。deleteDatabase返回的是IDBOpenDBRequest,其onsuccess/onerror是回调式 API,必须手动转成 Promise 才能与async/await协作。
  • Cookie 无法枚举,只能按已知名单逐个清除document.cookie只能写入而不能读取所有 Cookie 的完整列表(HttpOnly的 Cookie 更是不可见),因此需要一个cookiesToDelete应用白名单。示例中给出的sessionrefresh_tokenuser_prefs__stripe_mid覆盖了会话、令牌、偏好与第三方支付标记四类典型场景,实际项目中应替换为应用真实使用的 Cookie 名单。
  • Service Worker 缓存需要能力检测'caches' in window的守卫确保在不支持 Cache Storage API 的环境中不抛错;caches.keys()返回所有缓存名,逐个caches.delete()后同样用Promise.all并行处理。

原文档还给出了一条重要的时序纪律(在 right-to-erasure.mdx 中以 Warning 形式强调):

不要等服务端响应后再清理客户端数据。应在确认删除意图后立即清除本地数据。即使网络请求失败,用户的本地数据也已消失——这是正确的行为。服务端调用的重试与本地清理是两件独立的事。

五、服务端删除 API:接受请求并纳入 30 天窗口

客户端清理只解决了浏览器侧,服务端必须接收删除请求,并在 GDPR 规定的 30 天内异步完成处理。原文档给出的 Next.js App Router 路由实现如下:

// app/api/account/delete/route.ts (Next.js App Router) import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; export async function POST() { const session = await getServerSession(); if (!session?.user?.id) { return NextResponse.json({ error: 'Unauthenticated' }, { status: 401 }); } // Queue deletion — process asynchronously within 30 days await scheduleDeletion({ userId: session.user.id, requestedAt: new Date().toISOString(), // GDPR deadline: 30 calendar days from request deadline: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), }); // Send confirmation email to the address on record before it is deleted await sendDeletionConfirmationEmail(session.user.email); return NextResponse.json({ received: true }); }

实现要点拆解:

  • 身份校验先行getServerSession()拿不到会话(未登录)时立即返回401,删除接口必须拒绝匿名调用,否则会成为任意用户的删除攻击面;
  • 采用"排队 + 异步处理"而非同步删除scheduleDeletion()把删除任务连同requestedAt与计算好的deadlineDate.now() + 30 * 24 * 60 * 60 * 1000,即 30 个日历日)写入队列,由后台任务在窗口内完成删除。这样即便数据量大、涉及多个系统,也不会阻塞用户请求;
  • 在数据被删前发送确认邮件sendDeletionConfirmationEmail(session.user.email)必须在删除发生之前调用——这是唯一仍能联系到用户的时机,同时也为用户留存了受理凭证;
  • 幂等的受理响应{ received: true }表示"已受理"而非"已删除完成",语义上准确地区分了请求接收与最终执行。

六、确认与时间线沟通:让用户知道接下来会发生什么

删除请求提交后,原文档要求向用户明确传达四类信息:

  • 请求已被受理(request has been received);
  • 数据将在什么期限内被移除(例如 "within 30 days");
  • 用户已从所有设备登出(signed out of all devices);
  • 一个可供留存记录的参考号(reference number,如果系统支持)。

这四项沟通内容其实也是前文DeletionRequestButton完成态文案("Your deletion request has been received. Your data will be removed within 30 days. You have been signed out.")的落点:受理确认 + 30 天期限 + 登出提示,三者缺一不可。

第三方数据处理者:删除请求必须转发

原文档以 Warning 形式单独强调了第三方处理器问题:应用很可能把个人数据传递给分析(analytics)、CRM、邮件工具等第三方处理器,删除请求必须同步转发给这些处理器。每个处理器的 API 都应检查其删除端点,并把相应调用纳入删除工作流。常见做法是维护一张"处理器 → 删除端点"的清单,在scheduleDeletion的后台任务中逐一遍历调用——这与前文"每个处理器 API 检查删除端点"的建议互为表里。

七、与关联规则的协作:数据最小化是删除的"减负"

在规则体系中,right-to-erasure 并非孤立存在。right-to-erasure.mdx 的 frontmatter 声明了三条关联规则,其中与删除机制最互补的是 contenteditable="false">【免费下载链接】Front-End-Checklist🗂 The essential checklist for modern web development, for humans and AI agents项目地址: https://gitcode.com/gh_mirrors/fr/Front-End-Checklist

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 20:33:19

Agent生产落地五层架构与MCP/A2A实战避坑指南

1. 这张图谱不是“未来预测”&#xff0c;而是当下正在发生的产业切片你点开任何一篇讲Agent的文章&#xff0c;十有八九开头就是“Agent是AI的下一代范式”“2026年将全面爆发”。这话没错&#xff0c;但错在——它把正在剧烈演化的现实&#xff0c;包装成一张等待兑现的支票。…

作者头像 李华
网站建设 2026/9/19 20:31:21

AI论文写作工具千笔:提升科研效率的智能助手

1. 项目概述作为一名在学术圈摸爬滚打多年的研究者&#xff0c;我深知论文写作过程中的痛点。从文献检索到格式排版&#xff0c;每个环节都耗费大量时间。最近导师强力推荐的"千笔"AI论文工具&#xff0c;彻底改变了我的科研工作流。这款工具不仅整合了文献管理、写作…

作者头像 李华
网站建设 2026/9/19 20:31:00

MindSpore单卡LoRA微调大模型全流程实战

昇思MindSpore这个框架&#xff0c;真正上手做过大模型LoRA微调的人其实比想象中少。我最早是在一张24G显卡上拿7B模型做全参微调&#xff0c;显存直接爆掉&#xff0c;后来切到LoRA才把方案跑通&#xff0c;那段时间踩过的坑够写好几篇笔记。今天这篇就来盘一盘&#xff0c;用…

作者头像 李华