|
|
@@ -0,0 +1,70 @@
|
|
|
+/**
|
|
|
+ * 浏览器关闭时自动退出账号(sessionStorage 哨兵方案)
|
|
|
+ * 思路:
|
|
|
+ * - 每次页面加载:
|
|
|
+ * 1) 若检测到已登录且不存在“会话哨兵”,说明是上一次浏览器被关闭 → 先执行一次安全登出
|
|
|
+ * 2) 设置“会话哨兵”,标记本标签页生命周期内为存活状态
|
|
|
+ * - 刷新/同域SPA导航:不会丢失 sessionStorage 的哨兵,不会触发登出
|
|
|
+ * - 关闭浏览器/关闭所有同域标签:sessionStorage 被清空,哨兵丢失;下次打开时检测到无哨兵且此前已登录,则自动登出
|
|
|
+ *
|
|
|
+ * 优点:
|
|
|
+ * - 绝不影响刷新/导航等正常流程
|
|
|
+ * - 仅在浏览器真正关闭后、下次进入时执行一次安全登出
|
|
|
+ */
|
|
|
+
|
|
|
+import { useUserStore } from '@/pinia/user-store'
|
|
|
+import { changeToken } from '@/utils/cy-use/useChangeToken'
|
|
|
+
|
|
|
+/**
|
|
|
+ * 设置浏览器关闭时自动退出账号
|
|
|
+ * @returns 清理函数,用于移除事件监听器
|
|
|
+ */
|
|
|
+export function setupAutoLogoutOnClose() {
|
|
|
+ const SESSION_SENTINEL_KEY = '_browserSessionAlive'
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行退出登录的逻辑
|
|
|
+ * 使用项目现有的退出逻辑,确保与手动退出保持一致
|
|
|
+ */
|
|
|
+ const performLogout = () => {
|
|
|
+ try {
|
|
|
+ if (localStorage.token) {
|
|
|
+ localStorage.clear()
|
|
|
+ changeToken()
|
|
|
+ // 不做路由跳转,避免影响正常加载/关闭流程
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('自动退出时出错:', error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化:处理上一次是否为浏览器关闭
|
|
|
+ * 规则:
|
|
|
+ * - 若检测到已登录且不存在会话哨兵,说明上一次关闭了浏览器(而非刷新/导航)→ 先执行降级登出,确保安全
|
|
|
+ */
|
|
|
+ const handleInit = () => {
|
|
|
+ const hasToken = !!localStorage.token
|
|
|
+ const hasSentinel = sessionStorage.getItem(SESSION_SENTINEL_KEY) === '1'
|
|
|
+
|
|
|
+ if (hasToken && !hasSentinel) {
|
|
|
+ performLogout()
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置当前会话存活哨兵
|
|
|
+ sessionStorage.setItem(SESSION_SENTINEL_KEY, '1')
|
|
|
+ }
|
|
|
+
|
|
|
+ if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
|
|
+ handleInit()
|
|
|
+ } else {
|
|
|
+ const onLoad = () => {
|
|
|
+ handleInit()
|
|
|
+ window.removeEventListener('load', onLoad)
|
|
|
+ }
|
|
|
+ window.addEventListener('load', onLoad)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 哨兵方案无需事件监听,这里返回空清理函数以保持API一致
|
|
|
+ return () => {}
|
|
|
+}
|