完善状态验证

This commit is contained in:
2026-03-13 09:01:59 +08:00
parent a5304bdc9e
commit 3617a6a086
5 changed files with 233 additions and 292 deletions

View File

@@ -1,4 +1,4 @@
<template>
<template>
<view class="layout-root">
<view v-if="!isAuthReady" class="auth-loading-overlay" style="flex: 1; display: flex; align-items: center; justify-content: center; height: 100vh; background-color: #f5f5f5;">
<text style="color: #666; font-size: 16px;">身份鉴权中...</text>
@@ -79,7 +79,7 @@
</template>
<script setup lang="uts">
import { ref, computed, onMounted, watch } from 'vue'
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import AdminAside from '@/layouts/admin/components/AdminAside.uvue'
import AdminSubSider from '@/layouts/admin/components/AdminSubSider.uvue'
import AdminHeader from '@/layouts/admin/components/AdminHeader.uvue'
@@ -121,7 +121,8 @@ import {
import type { TabItem } from '@/layouts/admin/store/adminNavStore.uts'
import { getComponent } from '@/layouts/admin/router/adminComponentMap.uts'
import { hasAdminModuleAccess, refreshAdminRole } from '@/layouts/admin/utils/role.uts'
import { hasAdminModuleAccess } from '@/layouts/admin/utils/role.uts'
import { ensureAdminSession, handleSessionExpired } from '@/layouts/admin/utils/adminAuth.uts'
const props = defineProps({
currentPage: {
@@ -396,7 +397,26 @@ function onNotify(): void {
let resizeTid: any = null
onMounted(async () => {
await refreshAdminRole()
// 挂载鉴权相关的事件监听器,并在首次进入时阻断认证
try {
const isOk = await ensureAdminSession()
if (!isOk) {
return // 鉴权失败内部已处理跳转,终止后续挂载
}
} catch (e) {
handleSessionExpired()
return
}
uni.$on('AUTH_SESSION_EXPIRED', () => { handleSessionExpired() })
// 增加 visibilitychange 监听:在用户电脑休眠或者长时间放置重新激活时,核验会话
// #ifdef H5
if (typeof window !== 'undefined') {
window.addEventListener('visibilitychange', handleVisibilityChange)
}
// #endif
isAuthReady.value = true
initNavState()
if (props.currentPage != '') {
@@ -431,6 +451,34 @@ onMounted(async () => {
})
})
})
// ===== Auth State Handlers =====
let _lastVisibilityCheck = 0
const handleVisibilityChange = async () => {
// #ifdef H5
if (document.visibilityState === 'visible') {
const now = Date.now()
// 节流机制,防止频繁切换标签页产生过多校验
if (now - _lastVisibilityCheck < 10000) return
_lastVisibilityCheck = now
// 简易验证即可,如果底层 token 已经失效而 fetch 拿不到,就会报出 401 触发全局 AUTH_SESSION_EXPIRED
try {
await ensureAdminSession()
} catch(e) {}
}
// #endif
}
onUnmounted(() => {
uni.$off('AUTH_SESSION_EXPIRED')
// #ifdef H5
if (typeof window !== 'undefined') {
window.removeEventListener('visibilitychange', handleVisibilityChange)
}
// #endif
})
</script>
<style scoped lang="scss">

View File

@@ -0,0 +1,76 @@
import { state, getCurrentUser } from '@/utils/store.uts'
import { clearAdminRoleCache, refreshAdminRole } from './role.uts'
import supa from '@/components/supadb/aksupainstance.uts'
let __isHandlingExpired = false
/**
* 统一“登录过期”闭环处理
* - 防止重复弹窗
* - 清理所有认证、用户相关缓存
* - 重置状态树
* - 统一跳转回登录页
*/
export function handleSessionExpired(reason?: string) {
if (__isHandlingExpired) return
__isHandlingExpired = true
console.warn('[AdminAuth] 执行会话过期统一闭环:', reason ?? '未知原因')
// 1. 弹出提示 (确保只弹一次)
uni.showToast({
title: '登录已过期,请重新登录',
icon: 'none',
duration: 2000
})
// 2. 清除本地相关存储
try {
supa.signOut()
} catch(e){}
clearAdminRoleCache()
// 3. 重置全局业务状态树,防止其他组件看到旧内存残影
state.isLoggedIn = false
state.authUser = null
state.userProfile = { username: '', email: '' }
// 4. 跳转登录页
setTimeout(() => {
uni.reLaunch({
url: '/pages/user/login'
})
setTimeout(() => { __isHandlingExpired = false }, 1000)
}, 1000)
}
/**
* 统一的后台启动恢复授权流程
* 返回是否恢复/保持有效的登录态
*/
export async function ensureAdminSession(): Promise<boolean> {
try {
const sessionInfo = supa.getSession()
if (sessionInfo.session == null) {
console.warn('[AdminAuth] 没有发现凭证,要求重新登录')
handleSessionExpired('No credentials found')
return false
}
// 主动检查并补齐
const role = await refreshAdminRole()
if (role === 'unknown' || (state.userProfile != null && state.userProfile!.id == null)) {
// 等等,如果是断网状态呢?其实 store 里已经用 status <= 0 判断过断网了。
// 上一步在 store/getCurrentUser 时,如果返回 401 才会真正导致清空。
console.warn('[AdminAuth] Session被认为无效或用户确权失败')
handleSessionExpired('Role verification failed')
return false
}
return true
} catch (e) {
console.error('[AdminAuth] 鉴权启动异常:', e)
handleSessionExpired('Auth exception')
return false
}
}