退出登录
当用户退出网站时,他们是在表达完全退出个性化用户体验的意愿。因此,尽可能贴近用户的心理模型非常重要。例如,适当的退出体验还应考虑到用户在决定退出之前可能已打开的任何标签页。
良好退出体验的关键可以归纳为用户体验的视觉和状态方面的一致性。本指南提供了关于应注意什么以及如何实现良好退出体验的具体建议。
主要考虑因素
在您的网站上实施退出功能时,请注意以下方面,以确保流畅、安全和直观的退出过程
- 清晰且一致的退出用户体验:提供清晰且始终可见的退出按钮或链接,该按钮或链接在整个网站中易于识别和访问。避免使用模棱两可的标签,或者将退出功能隐藏在晦涩的菜单、子页面或其他不直观的位置。
- 确认提示:在最终确定退出过程之前实施确认提示。这可以帮助防止用户意外退出,并允许用户重新考虑他们是否真的需要退出——例如,如果他们勤奋地使用强密码或其他身份验证机制锁定其设备。
- 处理多个标签页:如果用户在不同的标签页中打开了来自同一网站的多个页面,请确保从一个标签页退出也会更新来自该网站的所有其他打开的标签页。
- 重定向到安全着陆页:成功退出后,将用户重定向到安全着陆页,该页面清楚地表明他们不再处于登录状态。避免将用户重定向到包含任何个性化信息的页面。同样,确保其他标签页也不再反映登录状态。此外,请确保您没有构建攻击者可以利用的 开放重定向。
- 会话清理:用户退出后,完全删除与用户会话关联的任何敏感用户会话数据、Cookie 或临时文件。这可以防止未经授权访问用户信息或帐户活动,并且还可以防止浏览器从其各种缓存(特别是 后退/前进缓存)中恢复包含敏感信息的页面。
- 错误处理和反馈:如果用户在退出时遇到任何问题,请向他们提供清晰的错误消息或反馈。如果退出过程失败,请告知他们任何潜在的安全风险或数据泄露。
- 无障碍功能注意事项:确保退出机制对于残障用户是可访问的,包括使用屏幕阅读器或键盘导航等辅助技术的用户。
- 跨浏览器兼容性:在不同的浏览器和设备上测试退出功能,以确保其工作一致且可靠。
- 持续监控和更新:定期监控退出过程是否存在任何潜在的漏洞或安全漏洞。实施及时的更新和补丁,以解决任何已识别的问题。
- 身份联合:如果用户使用 联合身份 登录,请查看是否 支持 且需要从身份提供商处退出。此外,如果身份提供商支持自动登录,请不要忘记 阻止它。
应该做的
- 如果您在退出流程(或其他访问撤销流程)中使服务器上的 Cookie 失效,请确保也删除用户设备上的 Cookie。
- 清理您可能已存储在用户设备上的任何敏感数据:Cookie、localStorage、sessionStorage、indexedDB、CacheStorage 以及任何其他本地数据存储。
- 确保返回的任何包含敏感数据(尤其是 HTML 文档)的资源都带有
Cache-control: no-store
HTTP 标头,以便浏览器不会将这些资源存储在永久存储中(例如,在磁盘上)。同样,返回敏感数据的 XHR/fetch
调用也应设置Cache-Control: no-store
HTTP 标头,以防止任何缓存。 - 确保用户设备上打开的任何标签页都与服务器端访问撤销保持同步。
退出时清理敏感数据
退出时,请考虑清除临时和本地存储的敏感数据。关注敏感数据的原因是,清除所有内容将导致用户体验显著下降,因为该用户很可能会再次回来。例如,如果您要清除所有本地存储的数据,那么您的用户将不得不重新确认 Cookie 同意提示,并像他们从未访问过您的网站一样经历其他过程。
如何清理 Cookie
在确认退出状态的页面的响应中,附加 Set-Cookie
HTTP 标头以清除与敏感数据相关或包含敏感数据的每个 Cookie。将 expires
值设置为遥远的过去日期,并将 Cookie 的值设置为空字符串,以确保万无一失。
Set-Cookie: sensitivecookie1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure
Set-Cookie: sensitivecookie2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; secure
...
离线场景
虽然上述方法对于一般用例来说已经足够,但如果用户处于离线状态,则该方法不起作用。您可能需要考虑需要两个 Cookie 来跟踪登录状态:一个安全的仅限 HTTPS 的 Cookie 和一个可通过 JavaScript 访问的常规 Cookie。如果您的用户尝试在离线时退出,您可以清除 JavaScript Cookie 并在可能的情况下继续执行其他清理操作。如果您有 Service Worker,您可能还想利用 Background Fetch API 在用户稍后在线时重试清除服务器状态的请求。
如何清理存储
在确认退出状态的页面的响应中,注意从各种数据存储中清理敏感数据
sessionStorage:虽然这会在用户终止与您网站的会话时被清除,但请考虑在用户退出时主动清理敏感数据,以防他们忘记关闭在您的网站上打开的所有标签页。
// Remove sensitive data from sessionStorage sessionStorage.removeItem('sensitiveSessionData1'); // ... // Or if everything in sessionStorage is sensitive, clear it all sessionStorage.clear();
localStorage、indexedDB、Cache/Service Worker API:当用户退出时,清理您可能已使用这些 API 存储的任何敏感数据,因为此类数据将在会话之间持久存在。
// Remove sensitive data from localStorage: localStorage.removeItem('sensitiveData1'); // ... // Or if everything in localStorage is sensitive, clear it all: localStorage.clear();
// Delete sensitive object stores in indexedDB: const name = 'exampleDB'; const version = 1; const request = indexedDB.open(name, version); request.onsuccess = (event) => { const db = request.result; db.deleteObjectStore('sensitiveStore1'); db.deleteObjectStore('sensitiveStore2'); // ... db.close(); }
// Delete sensitive resources stored via the Cache API: caches.open('cacheV1').then((cache) => { await cache.delete("/personal/profile.png"); // ... } // Or better yet, clear a cache bucket that contains sensitive resources: caches.delete('personalizedV1');
如何清理缓存
- HTTP 缓存:只要您在包含敏感数据的资源上设置
Cache-control: no-store
,HTTP 缓存就不会保留任何敏感内容。 - 后退/前进缓存:同样,如果您遵循了关于
Cache-control: no-store
以及在用户退出时清除敏感 Cookie(例如,与身份验证相关的安全 HTTPS-only Cookie)的建议,那么您无需担心敏感数据保留在后退/前进缓存中。实际上,如果后退/前进缓存功能观察到一个或多个以下信号,它将驱逐使用Cache-control: no-store
HTTP 标头提供的同源页面- 一个或多个安全 HTTPS-only Cookie 已被修改或删除。
- 页面发出的一个或多个 XHR/
fetch
调用的响应包含Cache-control: no-store
HTTP 标头。
跨标签页的一致用户体验
您的用户可能在决定退出之前为您的网站打开了许多标签页。到那时,他们可能已经忘记了其他标签页,甚至其他浏览器窗口。最好避免依赖用户关闭所有相关的标签页和窗口。相反,采取积极主动的姿态,确保用户的登录状态在各个标签页中保持一致。
如何操作
为了在各个标签页中实现一致的登录状态,请考虑结合使用 pageshow
/pagehide
事件和 Broadcast Channel API。
pageshow
事件:在持久化的pageshow
上,检查用户的登录状态,并在用户不再登录时清除敏感数据——甚至整个页面。请注意,pageshow
事件将在从后退/前进导航恢复时在页面首次呈现之前触发,这保证了您的登录状态检查将允许您将页面重置为非敏感状态。window.addEventListener('pageshow', (event) => { if (event.persisted && !document.cookie.match(/my-cookie)) { // The user has logged out. // Force a reload, or otherwise clear sensitive information right away. body.innerHTML = ''; location.reload(); } });
Broadcast Channel API:使用此 API 在标签页和窗口之间传达登录状态更改。如果用户已退出,请清除所有敏感数据,或者在所有包含敏感数据的标签页和窗口上重定向到退出页面。
// Upon logout, broadcast new login state so that other tabs can clean up too: const bc = new BroadcastChannel('login-state'); bc.postMessage('logged out'); // [...] const bc = new BroadcastChannel('login-state'); bc.onMessage = (msgevt) => { if (msgevt.data === 'logged out') { // Clean up, reload or navigate to the sign-out page. // ... } }
结论
通过遵循本文档中的指南,您将能够设计出色的退出用户体验,从而防止意外注销并保护用户的个人信息。