feat(auth): 添加异步密码哈希和验证函数
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
refactor(relay): 复用HTTP客户端和请求体序列化结果 feat(kernel): 添加获取单个审批记录的方法 fix(store): 改进SaaS连接错误分类和降级处理 docs: 更新审计文档和系统架构文档 refactor(prompt): 优化SQL查询参数化绑定 refactor(migration): 使用静态SQL和COALESCE更新配置项 feat(commands): 添加审批执行状态追踪和事件通知 chore: 更新启动脚本以支持Admin后台 fix(auth-guard): 优化授权状态管理和错误处理 refactor(db): 使用异步密码哈希函数 refactor(totp): 使用异步密码验证函数 style: 清理无用文件和注释 docs: 更新功能全景和审计文档 refactor(service): 优化HTTP客户端重用和请求处理 fix(connection): 改进SaaS不可用时的降级处理 refactor(handlers): 使用异步密码验证函数 chore: 更新依赖和工具链配置
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState, useCallback, type ReactNode } from 'react'
|
||||
import { useEffect, useState, useRef, useCallback, type ReactNode } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { isAuthenticated, getAccount, clearAuth } from '@/lib/auth'
|
||||
import { api, ApiRequestError } from '@/lib/api-client'
|
||||
@@ -18,50 +18,61 @@ export function AuthGuard({ children }: AuthGuardProps) {
|
||||
const [verifying, setVerifying] = useState(true)
|
||||
const [connectionError, setConnectionError] = useState(false)
|
||||
|
||||
// Ref 跟踪授权状态,避免 useCallback 闭包捕获过时的 state
|
||||
const authorizedRef = useRef(false)
|
||||
// 防止并发验证(RSC 导航可能触发多次 effect)
|
||||
const verifyingRef = useRef(false)
|
||||
|
||||
const verifyAuth = useCallback(async () => {
|
||||
// 防止并发验证
|
||||
if (verifyingRef.current) return
|
||||
verifyingRef.current = true
|
||||
setVerifying(true)
|
||||
setConnectionError(false)
|
||||
|
||||
if (!isAuthenticated()) {
|
||||
setVerifying(false)
|
||||
verifyingRef.current = false
|
||||
router.replace('/login')
|
||||
return
|
||||
}
|
||||
|
||||
// Already authorized? Skip re-verification on remount (e.g. Next.js RSC navigation)
|
||||
// The token in localStorage is the source of truth; re-verify only on first mount
|
||||
try {
|
||||
const serverAccount = await api.auth.me()
|
||||
setAccount(serverAccount)
|
||||
setAuthorized(true)
|
||||
authorizedRef.current = true
|
||||
} catch (err) {
|
||||
// Ignore abort errors — caused by navigation/SWR cancelling in-flight requests
|
||||
// Keep current authorized state intact
|
||||
// AbortError: 导航/SWR 取消了请求,忽略
|
||||
// 如果已有授权(ref 跟踪),保持不变;否则尝试 localStorage 缓存
|
||||
if (err instanceof DOMException && err.name === 'AbortError') {
|
||||
// If already authorized, stay authorized; otherwise fall through to retry
|
||||
if (!authorized) {
|
||||
// First mount was aborted — use cached account from localStorage
|
||||
if (!authorizedRef.current) {
|
||||
const cachedAccount = getAccount()
|
||||
if (cachedAccount) {
|
||||
setAccount(cachedAccount)
|
||||
setAuthorized(true)
|
||||
authorizedRef.current = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// Only clear auth on actual authentication failures (401/403)
|
||||
// Network errors, timeouts should NOT destroy the session
|
||||
// 401/403: 真正的认证失败,清除 token
|
||||
if (err instanceof ApiRequestError && (err.status === 401 || err.status === 403)) {
|
||||
clearAuth()
|
||||
authorizedRef.current = false
|
||||
router.replace('/login')
|
||||
} else {
|
||||
// Transient error — show retry UI, keep token in localStorage
|
||||
setConnectionError(true)
|
||||
// 网络错误/超时 — 仅在未授权时显示连接错误
|
||||
// 已授权的情况下忽略瞬态错误,保持当前状态
|
||||
if (!authorizedRef.current) {
|
||||
setConnectionError(true)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
setVerifying(false)
|
||||
verifyingRef.current = false
|
||||
}
|
||||
}, [router, authorized])
|
||||
}, [router])
|
||||
|
||||
useEffect(() => {
|
||||
verifyAuth()
|
||||
|
||||
Reference in New Issue
Block a user