完善状态验证
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="layout-root">
|
<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;">
|
<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>
|
<text style="color: #666; font-size: 16px;">身份鉴权中...</text>
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="uts">
|
<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 AdminAside from '@/layouts/admin/components/AdminAside.uvue'
|
||||||
import AdminSubSider from '@/layouts/admin/components/AdminSubSider.uvue'
|
import AdminSubSider from '@/layouts/admin/components/AdminSubSider.uvue'
|
||||||
import AdminHeader from '@/layouts/admin/components/AdminHeader.uvue'
|
import AdminHeader from '@/layouts/admin/components/AdminHeader.uvue'
|
||||||
@@ -121,7 +121,8 @@ import {
|
|||||||
import type { TabItem } from '@/layouts/admin/store/adminNavStore.uts'
|
import type { TabItem } from '@/layouts/admin/store/adminNavStore.uts'
|
||||||
|
|
||||||
import { getComponent } from '@/layouts/admin/router/adminComponentMap.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({
|
const props = defineProps({
|
||||||
currentPage: {
|
currentPage: {
|
||||||
@@ -396,7 +397,26 @@ function onNotify(): void {
|
|||||||
let resizeTid: any = null
|
let resizeTid: any = null
|
||||||
|
|
||||||
onMounted(async () => {
|
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
|
isAuthReady.value = true
|
||||||
initNavState()
|
initNavState()
|
||||||
if (props.currentPage != '') {
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
76
layouts/admin/utils/adminAuth.uts
Normal file
76
layouts/admin/utils/adminAuth.uts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,283 +1,101 @@
|
|||||||
|
GET http://localhost:5173/uni_modules/ak-req/ak-req.uts?t=1773363160904&import net::ERR_ABORTED 500 (Internal Server Error)
|
||||||
|
main.uts:16 [Vue warn]: Unhandled error during execution of async component loader
|
||||||
|
at <AsyncComponentWrapper>
|
||||||
|
at <PageBody>
|
||||||
|
at <Page>
|
||||||
|
at <Anonymous>
|
||||||
|
at <KeepAlive>
|
||||||
|
at <RouterView>
|
||||||
|
at <Layout>
|
||||||
|
at <App>
|
||||||
|
warnHandler @ uni-h5.es.js:19975
|
||||||
|
callWithErrorHandling @ vue.runtime.esm.js:1381
|
||||||
|
warn$1 @ vue.runtime.esm.js:1207
|
||||||
|
logError @ vue.runtime.esm.js:1438
|
||||||
|
errorHandler @ uni-h5.es.js:19600
|
||||||
|
callWithErrorHandling @ vue.runtime.esm.js:1381
|
||||||
|
handleError @ vue.runtime.esm.js:1421
|
||||||
|
onError @ vue.runtime.esm.js:3724
|
||||||
|
(anonymous) @ vue.runtime.esm.js:3767
|
||||||
|
Promise.catch
|
||||||
|
setup @ vue.runtime.esm.js:3766
|
||||||
|
callWithErrorHandling @ vue.runtime.esm.js:1381
|
||||||
|
setupStatefulComponent @ vue.runtime.esm.js:8985
|
||||||
|
setupComponent @ vue.runtime.esm.js:8946
|
||||||
|
mountComponent @ vue.runtime.esm.js:7262
|
||||||
|
processComponent @ vue.runtime.esm.js:7228
|
||||||
|
patch @ vue.runtime.esm.js:6694
|
||||||
|
mountChildren @ vue.runtime.esm.js:6942
|
||||||
|
processFragment @ vue.runtime.esm.js:7158
|
||||||
|
patch @ vue.runtime.esm.js:6668
|
||||||
|
mountChildren @ vue.runtime.esm.js:6942
|
||||||
|
processFragment @ vue.runtime.esm.js:7158
|
||||||
|
patch @ vue.runtime.esm.js:6668
|
||||||
|
mountChildren @ vue.runtime.esm.js:6942
|
||||||
|
mountElement @ vue.runtime.esm.js:6849
|
||||||
|
processElement @ vue.runtime.esm.js:6814
|
||||||
|
patch @ vue.runtime.esm.js:6682
|
||||||
|
mountChildren @ vue.runtime.esm.js:6942
|
||||||
|
mountElement @ vue.runtime.esm.js:6849
|
||||||
|
processElement @ vue.runtime.esm.js:6814
|
||||||
|
patch @ vue.runtime.esm.js:6682
|
||||||
|
mountChildren @ vue.runtime.esm.js:6942
|
||||||
|
processFragment @ vue.runtime.esm.js:7158
|
||||||
|
patch @ vue.runtime.esm.js:6668
|
||||||
|
componentUpdateFn @ vue.runtime.esm.js:7372
|
||||||
|
run @ vue.runtime.esm.js:153
|
||||||
|
instance.update @ vue.runtime.esm.js:7497
|
||||||
|
setupRenderEffect @ vue.runtime.esm.js:7507
|
||||||
|
mountComponent @ vue.runtime.esm.js:7274
|
||||||
|
processComponent @ vue.runtime.esm.js:7228
|
||||||
|
patch @ vue.runtime.esm.js:6694
|
||||||
|
mountChildren @ vue.runtime.esm.js:6942
|
||||||
|
mountElement @ vue.runtime.esm.js:6849
|
||||||
|
processElement @ vue.runtime.esm.js:6814
|
||||||
|
patch @ vue.runtime.esm.js:6682
|
||||||
|
componentUpdateFn @ vue.runtime.esm.js:7372
|
||||||
|
run @ vue.runtime.esm.js:153
|
||||||
|
instance.update @ vue.runtime.esm.js:7497
|
||||||
|
setupRenderEffect @ vue.runtime.esm.js:7507
|
||||||
|
mountComponent @ vue.runtime.esm.js:7274
|
||||||
|
processComponent @ vue.runtime.esm.js:7228
|
||||||
|
patch @ vue.runtime.esm.js:6694
|
||||||
|
componentUpdateFn @ vue.runtime.esm.js:7372
|
||||||
|
run @ vue.runtime.esm.js:153
|
||||||
|
instance.update @ vue.runtime.esm.js:7497
|
||||||
|
setupRenderEffect @ vue.runtime.esm.js:7507
|
||||||
|
mountComponent @ vue.runtime.esm.js:7274
|
||||||
|
processComponent @ vue.runtime.esm.js:7228
|
||||||
|
patch @ vue.runtime.esm.js:6694
|
||||||
|
componentUpdateFn @ vue.runtime.esm.js:7453
|
||||||
|
run @ vue.runtime.esm.js:153
|
||||||
|
instance.update @ vue.runtime.esm.js:7497
|
||||||
|
updateComponent @ vue.runtime.esm.js:7305
|
||||||
|
processComponent @ vue.runtime.esm.js:7239
|
||||||
|
patch @ vue.runtime.esm.js:6694
|
||||||
|
componentUpdateFn @ vue.runtime.esm.js:7453
|
||||||
|
run @ vue.runtime.esm.js:153
|
||||||
|
instance.update @ vue.runtime.esm.js:7497
|
||||||
|
callWithErrorHandling @ vue.runtime.esm.js:1381
|
||||||
|
flushJobs @ vue.runtime.esm.js:1585
|
||||||
|
Promise.then
|
||||||
|
queueFlush @ vue.runtime.esm.js:1494
|
||||||
|
queueJob @ vue.runtime.esm.js:1488
|
||||||
|
scheduler @ vue.runtime.esm.js:3179
|
||||||
|
resetScheduling @ vue.runtime.esm.js:236
|
||||||
|
triggerEffects @ vue.runtime.esm.js:280
|
||||||
|
triggerRefValue @ vue.runtime.esm.js:1033
|
||||||
|
set value @ vue.runtime.esm.js:1078
|
||||||
|
finalizeNavigation @ vue-router.mjs?v=ed041164:2474
|
||||||
|
(anonymous) @ vue-router.mjs?v=ed041164:2384
|
||||||
|
Promise.then
|
||||||
|
pushWithRedirect @ vue-router.mjs?v=ed041164:2352
|
||||||
|
push @ vue-router.mjs?v=ed041164:2278
|
||||||
|
install @ vue-router.mjs?v=ed041164:2631
|
||||||
|
use @ vue.runtime.esm.js:5190
|
||||||
|
initRouter @ uni-h5.es.js:19886
|
||||||
|
install @ uni-h5.es.js:19955
|
||||||
|
use @ vue.runtime.esm.js:5190
|
||||||
|
(anonymous) @ main.uts:16
|
||||||
|
main.uts:16 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/homePage/index.uvue?t=1773363235472&import》
|
||||||
|
|
||||||
list.uvue:526 Coupon list initializing and fetching data...
|
|
||||||
13
|
|
||||||
role.uts:59 [AdminRole] 未能获取到有效的管理端角色,准备安全降级...
|
|
||||||
role.uts:59 [AdminRole] 未能获取到有效的管理端角色,准备安全降级...
|
|
||||||
login.uvue:373 signIn result:
|
|
||||||
AkSupaSignInResult {access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmO…xzZX0.YFcXQloKqsalFsOktCsDQUWPwvP8d_B58ss_SznxwZs', refresh_token: 'gdsl27rjhn62', expires_at: 1773284440, user: UTSJSONObject2, token_type: 'bearer', …}
|
|
||||||
access_token
|
|
||||||
:
|
|
||||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmOTkyZGZmYS1hOGZkLTQ1YmItODY3MC02ZmVlNWE1YWU4NGQiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzczMjg0NDQwLCJpYXQiOjE3NzMyODA4NDAsImVtYWlsIjoiYWRtaW5AMTYzLmNvbSIsInBob25lIjoiIiwiYXBwX21ldGFkYXRhIjp7InByb3ZpZGVyIjoiZW1haWwiLCJwcm92aWRlcnMiOlsiZW1haWwiXX0sInVzZXJfbWV0YWRhdGEiOnsiZW1haWwiOiJhZG1pbkAxNjMuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInBob25lX3ZlcmlmaWVkIjpmYWxzZSwic3ViIjoiZjk5MmRmZmEtYThmZC00NWJiLTg2NzAtNmZlZTVhNWFlODRkIiwidXNlcl9yb2xlIjoibWVyY2hhbnQifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc3MzI4MDg0MH1dLCJzZXNzaW9uX2lkIjoiMTJhMmEyZjgtZWU1ZC00OWZjLWIwOTAtOTBlNmIzNWMxZGJhIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.YFcXQloKqsalFsOktCsDQUWPwvP8d_B58ss_SznxwZs"
|
|
||||||
expires_at
|
|
||||||
:
|
|
||||||
1773284440
|
|
||||||
expires_in
|
|
||||||
:
|
|
||||||
3600
|
|
||||||
raw
|
|
||||||
:
|
|
||||||
UTSJSONObject2
|
|
||||||
access_token
|
|
||||||
:
|
|
||||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmOTkyZGZmYS1hOGZkLTQ1YmItODY3MC02ZmVlNWE1YWU4NGQiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzczMjg0NDQwLCJpYXQiOjE3NzMyODA4NDAsImVtYWlsIjoiYWRtaW5AMTYzLmNvbSIsInBob25lIjoiIiwiYXBwX21ldGFkYXRhIjp7InByb3ZpZGVyIjoiZW1haWwiLCJwcm92aWRlcnMiOlsiZW1haWwiXX0sInVzZXJfbWV0YWRhdGEiOnsiZW1haWwiOiJhZG1pbkAxNjMuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInBob25lX3ZlcmlmaWVkIjpmYWxzZSwic3ViIjoiZjk5MmRmZmEtYThmZC00NWJiLTg2NzAtNmZlZTVhNWFlODRkIiwidXNlcl9yb2xlIjoibWVyY2hhbnQifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc3MzI4MDg0MH1dLCJzZXNzaW9uX2lkIjoiMTJhMmEyZjgtZWU1ZC00OWZjLWIwOTAtOTBlNmIzNWMxZGJhIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.YFcXQloKqsalFsOktCsDQUWPwvP8d_B58ss_SznxwZs"
|
|
||||||
expires_at
|
|
||||||
:
|
|
||||||
1773284440
|
|
||||||
expires_in
|
|
||||||
:
|
|
||||||
3600
|
|
||||||
refresh_token
|
|
||||||
:
|
|
||||||
"gdsl27rjhn62"
|
|
||||||
token_type
|
|
||||||
:
|
|
||||||
"bearer"
|
|
||||||
user
|
|
||||||
:
|
|
||||||
UTSJSONObject2 {id: 'f992dffa-a8fd-45bb-8670-6fee5a5ae84d', aud: 'authenticated', role: 'authenticated', email: 'admin@163.com', email_confirmed_at: '2026-03-12T01:25:56.424096Z', …}
|
|
||||||
weak_password
|
|
||||||
:
|
|
||||||
null
|
|
||||||
forEach
|
|
||||||
:
|
|
||||||
ƒ forEach(callback)
|
|
||||||
get
|
|
||||||
:
|
|
||||||
ƒ get(key)
|
|
||||||
getAny
|
|
||||||
:
|
|
||||||
ƒ getAny(key, defaultValue)
|
|
||||||
getArray
|
|
||||||
:
|
|
||||||
ƒ getArray(key, defaultValue)
|
|
||||||
getBoolean
|
|
||||||
:
|
|
||||||
ƒ getBoolean(key, defaultValue)
|
|
||||||
getJSON
|
|
||||||
:
|
|
||||||
ƒ getJSON(key, defaultValue)
|
|
||||||
getNumber
|
|
||||||
:
|
|
||||||
ƒ getNumber(key, defaultValue)
|
|
||||||
getString
|
|
||||||
:
|
|
||||||
ƒ getString(key, defaultValue)
|
|
||||||
set
|
|
||||||
:
|
|
||||||
ƒ set(key, value)
|
|
||||||
toJSON
|
|
||||||
:
|
|
||||||
undefined
|
|
||||||
toMap
|
|
||||||
:
|
|
||||||
ƒ toMap()
|
|
||||||
_getValue
|
|
||||||
:
|
|
||||||
ƒ _getValue(keyPath, defaultValue)
|
|
||||||
_resolveKeyPath
|
|
||||||
:
|
|
||||||
ƒ _resolveKeyPath(keyPath)
|
|
||||||
[[Prototype]]
|
|
||||||
:
|
|
||||||
Object
|
|
||||||
refresh_token
|
|
||||||
:
|
|
||||||
"gdsl27rjhn62"
|
|
||||||
token_type
|
|
||||||
:
|
|
||||||
"bearer"
|
|
||||||
user
|
|
||||||
:
|
|
||||||
UTSJSONObject2 {id: 'f992dffa-a8fd-45bb-8670-6fee5a5ae84d', aud: 'authenticated', role: 'authenticated', email: 'admin@163.com', email_confirmed_at: '2026-03-12T01:25:56.424096Z', …}
|
|
||||||
$UTSMetadata$
|
|
||||||
:
|
|
||||||
(...)
|
|
||||||
[[Prototype]]
|
|
||||||
:
|
|
||||||
UTSType
|
|
||||||
login.uvue:176 🔍 开始校验商家端角色 -> UID: f992dffa-a8fd-45bb-8670-6fee5a5ae84d, Email: admin@163.com
|
|
||||||
login.uvue:186 ✅ 按 auth_id 匹配成功,role: admin
|
|
||||||
login.uvue:449 登录错误: Error: 您还没有注册商家端账户,快去注册一个
|
|
||||||
at login.uvue:403:10
|
|
||||||
at Generator.next (<anonymous>)
|
|
||||||
login.uvue:373 signIn result:
|
|
||||||
AkSupaSignInResult {access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmO…xzZX0.0RArWcn148XkRpW9C0vwboAcvpem4KRz6-OO0vAE4RU', refresh_token: 'kgzfaeokz4r2', expires_at: 1773284456, user: UTSJSONObject2, token_type: 'bearer', …}
|
|
||||||
access_token
|
|
||||||
:
|
|
||||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmOTkyZGZmYS1hOGZkLTQ1YmItODY3MC02ZmVlNWE1YWU4NGQiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzczMjg0NDU2LCJpYXQiOjE3NzMyODA4NTYsImVtYWlsIjoiYWRtaW5AMTYzLmNvbSIsInBob25lIjoiIiwiYXBwX21ldGFkYXRhIjp7InByb3ZpZGVyIjoiZW1haWwiLCJwcm92aWRlcnMiOlsiZW1haWwiXX0sInVzZXJfbWV0YWRhdGEiOnsiZW1haWwiOiJhZG1pbkAxNjMuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInBob25lX3ZlcmlmaWVkIjpmYWxzZSwic3ViIjoiZjk5MmRmZmEtYThmZC00NWJiLTg2NzAtNmZlZTVhNWFlODRkIiwidXNlcl9yb2xlIjoibWVyY2hhbnQifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc3MzI4MDg1Nn1dLCJzZXNzaW9uX2lkIjoiN2NlZWYwZjQtNmNlOS00ZDE5LWJjYmItNzFhNDVmOTRiZTI2IiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.0RArWcn148XkRpW9C0vwboAcvpem4KRz6-OO0vAE4RU"
|
|
||||||
expires_at
|
|
||||||
:
|
|
||||||
1773284456
|
|
||||||
expires_in
|
|
||||||
:
|
|
||||||
3600
|
|
||||||
raw
|
|
||||||
:
|
|
||||||
UTSJSONObject2
|
|
||||||
access_token
|
|
||||||
:
|
|
||||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmOTkyZGZmYS1hOGZkLTQ1YmItODY3MC02ZmVlNWE1YWU4NGQiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzczMjg0NDU2LCJpYXQiOjE3NzMyODA4NTYsImVtYWlsIjoiYWRtaW5AMTYzLmNvbSIsInBob25lIjoiIiwiYXBwX21ldGFkYXRhIjp7InByb3ZpZGVyIjoiZW1haWwiLCJwcm92aWRlcnMiOlsiZW1haWwiXX0sInVzZXJfbWV0YWRhdGEiOnsiZW1haWwiOiJhZG1pbkAxNjMuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInBob25lX3ZlcmlmaWVkIjpmYWxzZSwic3ViIjoiZjk5MmRmZmEtYThmZC00NWJiLTg2NzAtNmZlZTVhNWFlODRkIiwidXNlcl9yb2xlIjoibWVyY2hhbnQifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc3MzI4MDg1Nn1dLCJzZXNzaW9uX2lkIjoiN2NlZWYwZjQtNmNlOS00ZDE5LWJjYmItNzFhNDVmOTRiZTI2IiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.0RArWcn148XkRpW9C0vwboAcvpem4KRz6-OO0vAE4RU"
|
|
||||||
expires_at
|
|
||||||
:
|
|
||||||
1773284456
|
|
||||||
expires_in
|
|
||||||
:
|
|
||||||
3600
|
|
||||||
refresh_token
|
|
||||||
:
|
|
||||||
"kgzfaeokz4r2"
|
|
||||||
token_type
|
|
||||||
:
|
|
||||||
"bearer"
|
|
||||||
user
|
|
||||||
:
|
|
||||||
UTSJSONObject2 {id: 'f992dffa-a8fd-45bb-8670-6fee5a5ae84d', aud: 'authenticated', role: 'authenticated', email: 'admin@163.com', email_confirmed_at: '2026-03-12T01:25:56.424096Z', …}
|
|
||||||
weak_password
|
|
||||||
:
|
|
||||||
null
|
|
||||||
forEach
|
|
||||||
:
|
|
||||||
ƒ forEach(callback)
|
|
||||||
get
|
|
||||||
:
|
|
||||||
ƒ get(key)
|
|
||||||
getAny
|
|
||||||
:
|
|
||||||
ƒ getAny(key, defaultValue)
|
|
||||||
getArray
|
|
||||||
:
|
|
||||||
ƒ getArray(key, defaultValue)
|
|
||||||
getBoolean
|
|
||||||
:
|
|
||||||
ƒ getBoolean(key, defaultValue)
|
|
||||||
getJSON
|
|
||||||
:
|
|
||||||
ƒ getJSON(key, defaultValue)
|
|
||||||
getNumber
|
|
||||||
:
|
|
||||||
ƒ getNumber(key, defaultValue)
|
|
||||||
getString
|
|
||||||
:
|
|
||||||
ƒ getString(key, defaultValue)
|
|
||||||
set
|
|
||||||
:
|
|
||||||
ƒ set(key, value)
|
|
||||||
toJSON
|
|
||||||
:
|
|
||||||
undefined
|
|
||||||
toMap
|
|
||||||
:
|
|
||||||
ƒ toMap()
|
|
||||||
_getValue
|
|
||||||
:
|
|
||||||
ƒ _getValue(keyPath, defaultValue)
|
|
||||||
_resolveKeyPath
|
|
||||||
:
|
|
||||||
ƒ _resolveKeyPath(keyPath)
|
|
||||||
[[Prototype]]
|
|
||||||
:
|
|
||||||
Object
|
|
||||||
refresh_token
|
|
||||||
:
|
|
||||||
"kgzfaeokz4r2"
|
|
||||||
token_type
|
|
||||||
:
|
|
||||||
"bearer"
|
|
||||||
user
|
|
||||||
:
|
|
||||||
UTSJSONObject2
|
|
||||||
app_metadata
|
|
||||||
:
|
|
||||||
UTSJSONObject2 {provider: 'email', providers: Array(1), toJSON: undefined, _resolveKeyPath: ƒ, _getValue: ƒ, …}
|
|
||||||
aud
|
|
||||||
:
|
|
||||||
"authenticated"
|
|
||||||
confirmed_at
|
|
||||||
:
|
|
||||||
"2026-03-12T01:25:56.424096Z"
|
|
||||||
created_at
|
|
||||||
:
|
|
||||||
"2026-03-12T01:25:56.397092Z"
|
|
||||||
email
|
|
||||||
:
|
|
||||||
"admin@163.com"
|
|
||||||
email_confirmed_at
|
|
||||||
:
|
|
||||||
"2026-03-12T01:25:56.424096Z"
|
|
||||||
id
|
|
||||||
:
|
|
||||||
"f992dffa-a8fd-45bb-8670-6fee5a5ae84d"
|
|
||||||
identities
|
|
||||||
:
|
|
||||||
[UTSJSONObject2]
|
|
||||||
is_anonymous
|
|
||||||
:
|
|
||||||
false
|
|
||||||
last_sign_in_at
|
|
||||||
:
|
|
||||||
"2026-03-12T02:00:56.659679275Z"
|
|
||||||
phone
|
|
||||||
:
|
|
||||||
""
|
|
||||||
role
|
|
||||||
:
|
|
||||||
"authenticated"
|
|
||||||
updated_at
|
|
||||||
:
|
|
||||||
"2026-03-12T02:00:56.667158Z"
|
|
||||||
user_metadata
|
|
||||||
:
|
|
||||||
UTSJSONObject2 {email: 'admin@163.com', email_verified: true, phone_verified: false, sub: 'f992dffa-a8fd-45bb-8670-6fee5a5ae84d', user_role: 'merchant', …}
|
|
||||||
forEach
|
|
||||||
:
|
|
||||||
ƒ forEach(callback)
|
|
||||||
get
|
|
||||||
:
|
|
||||||
ƒ get(key)
|
|
||||||
getAny
|
|
||||||
:
|
|
||||||
ƒ getAny(key, defaultValue)
|
|
||||||
getArray
|
|
||||||
:
|
|
||||||
ƒ getArray(key, defaultValue)
|
|
||||||
getBoolean
|
|
||||||
:
|
|
||||||
ƒ getBoolean(key, defaultValue)
|
|
||||||
getJSON
|
|
||||||
:
|
|
||||||
ƒ getJSON(key, defaultValue)
|
|
||||||
getNumber
|
|
||||||
:
|
|
||||||
ƒ getNumber(key, defaultValue)
|
|
||||||
getString
|
|
||||||
:
|
|
||||||
ƒ getString(key, defaultValue)
|
|
||||||
set
|
|
||||||
:
|
|
||||||
ƒ set(key, value)
|
|
||||||
toJSON
|
|
||||||
:
|
|
||||||
undefined
|
|
||||||
toMap
|
|
||||||
:
|
|
||||||
ƒ toMap()
|
|
||||||
_getValue
|
|
||||||
:
|
|
||||||
ƒ _getValue(keyPath, defaultValue)
|
|
||||||
_resolveKeyPath
|
|
||||||
:
|
|
||||||
ƒ _resolveKeyPath(keyPath)
|
|
||||||
[[Prototype]]
|
|
||||||
:
|
|
||||||
Object
|
|
||||||
$UTSMetadata$
|
|
||||||
:
|
|
||||||
(...)
|
|
||||||
[[Prototype]]
|
|
||||||
:
|
|
||||||
UTSType
|
|
||||||
login.uvue:176 🔍 开始校验商家端角色 -> UID: f992dffa-a8fd-45bb-8670-6fee5a5ae84d, Email: admin@163.com
|
|
||||||
login.uvue:186 ✅ 按 auth_id 匹配成功,role: admin
|
|
||||||
login.uvue:449 登录错误: Error: 您还没有注册商家端账户,快去注册一个
|
|
||||||
at login.uvue:403:10
|
|
||||||
at Generator.next (<anonymous>)
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AkReqUploadOptions, AkReqOptions, AkReqResponse, AkReqError } from './interface.uts';
|
import { AkReqUploadOptions, AkReqOptions, AkReqResponse, AkReqError } from './interface.uts';
|
||||||
import { SUPA_URL } from '@/ak/config.uts';
|
import { SUPA_URL } from '@/ak/config.uts';
|
||||||
|
|
||||||
// token 持久化 key
|
// token 持久化 key
|
||||||
@@ -117,10 +117,7 @@ export class AkReq {
|
|||||||
if (accessToken !== null && refreshTokenNew !== null && expiresAt !== null) {
|
if (accessToken !== null && refreshTokenNew !== null && expiresAt !== null) {
|
||||||
this.setToken(accessToken, refreshTokenNew, expiresAt);
|
this.setToken(accessToken, refreshTokenNew, expiresAt);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else { this.clearToken(); uni.$emit('AUTH_SESSION_EXPIRED', { reason: 'refresh_failed' }); return false; }
|
||||||
this.clearToken();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.clearToken();
|
this.clearToken();
|
||||||
return false;
|
return false;
|
||||||
@@ -249,6 +246,8 @@ export class AkReq {
|
|||||||
// 全局处理 401 未授权:在非 refresh 场景下,清理 token。
|
// 全局处理 401 未授权:在非 refresh 场景下,清理 token。
|
||||||
// 测试模式下不强制跳登录页,避免影响任意跳转调试。
|
// 测试模式下不强制跳登录页,避免影响任意跳转调试。
|
||||||
if ((finalRes.status === 401) && (skipRefresh !== true)) {
|
if ((finalRes.status === 401) && (skipRefresh !== true)) {
|
||||||
|
uni.$emit('AUTH_SESSION_EXPIRED', { reason: '401' });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.clearToken();
|
this.clearToken();
|
||||||
uni.showToast({ title: '未授权或登录已过期,请重新登录', icon: 'none' });
|
uni.showToast({ title: '未授权或登录已过期,请重新登录', icon: 'none' });
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import supa, { supaReady } from '@/components/supadb/aksupainstance.uts'
|
import supa, { supaReady } from '@/components/supadb/aksupainstance.uts'
|
||||||
import type { UserProfile, UserStats } from '@/pages/user/types.uts'
|
import type { UserProfile, UserStats } from '@/pages/user/types.uts'
|
||||||
import type { DeviceInfo } from '@/pages/sense/types.uts'
|
import type { DeviceInfo } from '@/pages/sense/types.uts'
|
||||||
import { SenseDataService, type DeviceParams } from '@/pages/sense/senseDataService.uts'
|
import { SenseDataService, type DeviceParams } from '@/pages/sense/senseDataService.uts'
|
||||||
|
|||||||
Reference in New Issue
Block a user