Skip to content

Commit 3ba1e48

Browse files
committed
优化通知系统安全性
1 parent 1f0d712 commit 3ba1e48

File tree

4 files changed

+123
-1
lines changed

4 files changed

+123
-1
lines changed

dev-dist/sw.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ define(['./workbox-c6a197bf'], (function (workbox) { 'use strict';
8080
*/
8181
workbox.precacheAndRoute([{
8282
"url": "/index.html",
83-
"revision": "0.b2q02mbod7"
83+
"revision": "0.qilcmqrpcfg"
8484
}], {});
8585
workbox.cleanupOutdatedCaches();
8686
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/index.html"), {

src/App.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
<!-- PWA提示组件 -->
1313
<PWAPrompts />
14+
15+
<!-- 通知推送账户检查组件 -->
16+
<NotificationAccountChecker />
1417
</template>
1518

1619
<script setup>
@@ -25,6 +28,7 @@ import SimpleLayout from "@/layouts/SimpleLayout.vue";
2528
import GlobalSnackbar from "@/components/GlobalSnackbar.vue";
2629
import NotificationReminderSnackbar from "@/components/shared/NotificationReminderSnackbar.vue";
2730
import PWAPrompts from "@/components/PWAPrompts.vue";
31+
import NotificationAccountChecker from "@/components/NotificationAccountChecker.vue";
2832
2933
const route = useRoute();
3034
const router = useRouter();
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<template>
2+
<!-- 这个组件在后台运行,不需要UI -->
3+
</template>
4+
5+
<script setup>
6+
import { watch, onMounted, onUnmounted } from 'vue'
7+
import { localuser } from '@/services/localAccount'
8+
import { pushNotificationService } from '@/services/pushNotificationService'
9+
import { getPushSubscriptions } from '@/services/notificationService'
10+
11+
let checkInterval = null
12+
13+
// 检查推送通知是否匹配当前账户
14+
const checkPushNotificationOwnership = async () => {
15+
try {
16+
// 如果用户未登录,无需检查
17+
if (!localuser.isLogin.value) {
18+
console.log('用户未登录,跳过推送通知检查')
19+
return
20+
}
21+
22+
const currentUserId = localuser.user.value.id
23+
if (!currentUserId || currentUserId === 0) {
24+
console.warn('当前用户ID无效,无法检查推送通知')
25+
return
26+
}
27+
28+
// 获取本地推送订阅状态
29+
const localStatus = await pushNotificationService.getSubscriptionStatus()
30+
31+
// 如果本地没有订阅,无需检查
32+
if (!localStatus.subscribed || !localStatus.subscription) {
33+
console.log('本地没有有效的推送订阅,跳过检查')
34+
return
35+
}
36+
37+
// 获取服务器端的推送订阅列表
38+
const serverSubscriptions = await getPushSubscriptions()
39+
if (!serverSubscriptions.subscriptions || !Array.isArray(serverSubscriptions.subscriptions)) {
40+
console.warn('服务器推送订阅数据无效,无法检查')
41+
return
42+
}
43+
44+
// 查找当前设备的订阅
45+
const currentEndpoint = localStatus.subscription.endpoint
46+
const matchingSubscription = serverSubscriptions.subscriptions.find(
47+
48+
sub => sub.endpoint === currentEndpoint
49+
)
50+
51+
if (matchingSubscription) {
52+
// 检查订阅是否属于当前用户
53+
if (matchingSubscription.user_id !== currentUserId) {
54+
console.warn(`推送通知属于用户 ${matchingSubscription.user_id},但当前用户是 ${currentUserId},正在注销...`)
55+
56+
// 注销不匹配的推送通知
57+
await pushNotificationService.unsubscribe()
58+
console.log('已自动注销不匹配账户的推送通知')
59+
} else {
60+
console.log('推送通知账户匹配正常')
61+
}
62+
}
63+
} catch (error) {
64+
console.warn('检查推送通知账户时出错:', error)
65+
}
66+
}
67+
68+
// 监听用户登录状态变化
69+
watch(
70+
() => localuser.isLogin.value,
71+
(isLoggedIn) => {
72+
if (isLoggedIn) {
73+
console.log('用户已登录,开始检查推送通知账户匹配')
74+
// 用户登录后,延迟检查推送通知
75+
setTimeout(checkPushNotificationOwnership, 2000)
76+
}
77+
},
78+
{ immediate: true }
79+
)
80+
81+
// 监听用户信息变化
82+
watch(
83+
() => localuser.user.value.id,
84+
(newUserId, oldUserId) => {
85+
86+
// 如果用户ID发生变化且都不为0,说明切换了账户
87+
if (newUserId && oldUserId && newUserId !== oldUserId && oldUserId !== 0) {
88+
console.log(`账户从 ${oldUserId} 切换到 ${newUserId},检查推送通知`)
89+
setTimeout(checkPushNotificationOwnership, 1000)
90+
}
91+
}
92+
)
93+
94+
onMounted(() => {
95+
// 组件挂载后立即检查一次
96+
if (localuser.isLogin.value) {
97+
setTimeout(checkPushNotificationOwnership, 3000)
98+
}
99+
100+
// 每5分钟定期检查一次
101+
checkInterval = setInterval(checkPushNotificationOwnership, 5 * 60 * 1000)
102+
})
103+
104+
onUnmounted(() => {
105+
if (checkInterval) {
106+
clearInterval(checkInterval)
107+
}
108+
})
109+
</script>

src/services/localAccount.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,15 @@ const logout = async (logoutFromServer = true) => {
388388
}
389389
}
390390

391+
// 注销推送通知
392+
try {
393+
const { pushNotificationService } = await import('./pushNotificationService.js');
394+
await pushNotificationService.unsubscribe();
395+
console.log('推送通知已在退出时注销');
396+
} catch (error) {
397+
console.warn('退出时注销推送通知失败:', error);
398+
}
399+
391400
localStorage.removeItem(TOKEN_KEY);
392401
localStorage.removeItem(REFRESH_TOKEN_KEY);
393402
localStorage.removeItem(TOKEN_EXPIRES_AT_KEY);

0 commit comments

Comments
 (0)