111
This commit is contained in:
202
src/components/AuthGuard.tsx
Normal file
202
src/components/AuthGuard.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { Spin } from 'antd'
|
||||
import type { AppDispatch } from '../store/store'
|
||||
import {
|
||||
login,
|
||||
verifyToken,
|
||||
restoreAuthState,
|
||||
clearError,
|
||||
clearAuthState,
|
||||
selectIsAuthenticated,
|
||||
selectAuthLoading,
|
||||
selectAuthError,
|
||||
selectToken
|
||||
} from '../store/slices/authSlice'
|
||||
import LoginPage from './LoginPage'
|
||||
import InstallPage from './InstallPage'
|
||||
import apiClient from '../services/apiClient'
|
||||
|
||||
interface AuthGuardProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证守卫组件
|
||||
* 负责验证用户登录状态,未登录时显示登录页面
|
||||
*/
|
||||
const AuthGuard: React.FC<AuthGuardProps> = ({ children }) => {
|
||||
const dispatch = useDispatch<AppDispatch>()
|
||||
const isAuthenticated = useSelector(selectIsAuthenticated)
|
||||
const authLoading = useSelector(selectAuthLoading)
|
||||
const authError = useSelector(selectAuthError)
|
||||
const token = useSelector(selectToken)
|
||||
|
||||
const [isInitializing, setIsInitializing] = useState(true)
|
||||
const [loginError, setLoginError] = useState<string | null>(null)
|
||||
const [systemInitialized, setSystemInitialized] = useState<boolean | null>(null)
|
||||
|
||||
// 调试:监听认证状态变化
|
||||
useEffect(() => {
|
||||
console.log('🔐 AuthGuard - 认证状态变化:', {
|
||||
isAuthenticated,
|
||||
authLoading,
|
||||
token: token ? '***' : null,
|
||||
isInitializing,
|
||||
systemInitialized
|
||||
})
|
||||
}, [isAuthenticated, authLoading, token, isInitializing, systemInitialized])
|
||||
|
||||
// 组件挂载时检查系统初始化状态
|
||||
useEffect(() => {
|
||||
const initializeAuth = async () => {
|
||||
try {
|
||||
console.log('🔐 检查系统初始化状态...')
|
||||
|
||||
// 首先检查系统是否已初始化
|
||||
// const initResult = await apiClient.get<any>('/')
|
||||
const initResult = await apiClient.get<any>('/api/auth/check-initialization')
|
||||
|
||||
if (initResult.success) {
|
||||
setSystemInitialized(initResult.isInitialized)
|
||||
console.log(`🔐 系统初始化状态: ${initResult.isInitialized ? '已初始化' : '未初始化'}`)
|
||||
|
||||
// 如果系统已初始化,继续认证流程
|
||||
if (initResult.isInitialized) {
|
||||
console.log('🔐 初始化认证状态...')
|
||||
|
||||
// 先尝试从本地存储恢复状态
|
||||
dispatch(restoreAuthState())
|
||||
|
||||
// 等待一个tick让状态更新
|
||||
await new Promise(resolve => setTimeout(resolve, 0))
|
||||
|
||||
// 获取恢复后的token
|
||||
const currentToken = localStorage.getItem('auth_token')
|
||||
|
||||
if (currentToken) {
|
||||
console.log('🔐 找到本地token,验证有效性...')
|
||||
// 验证token是否仍然有效
|
||||
try {
|
||||
const result = await dispatch(verifyToken(currentToken))
|
||||
|
||||
if (verifyToken.fulfilled.match(result)) {
|
||||
console.log('✅ Token验证成功')
|
||||
} else {
|
||||
console.log('❌ Token验证失败:', result.payload)
|
||||
setLoginError('登录已过期,请重新登录')
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('❌ Token验证出错:', error)
|
||||
setLoginError('登录验证失败,请重新登录')
|
||||
}
|
||||
} else {
|
||||
console.log('🔐 未找到本地token')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error('检查系统初始化状态失败')
|
||||
setSystemInitialized(false) // 默认为未初始化
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化检查失败:', error)
|
||||
setSystemInitialized(false) // 出错时默认为未初始化
|
||||
} finally {
|
||||
setIsInitializing(false)
|
||||
}
|
||||
}
|
||||
|
||||
initializeAuth()
|
||||
}, [dispatch])
|
||||
|
||||
// 监听token过期事件
|
||||
useEffect(() => {
|
||||
const handleTokenExpired = () => {
|
||||
console.log('🔐 Token过期,清除认证状态')
|
||||
dispatch(clearAuthState())
|
||||
setLoginError('登录已过期,请重新登录')
|
||||
}
|
||||
|
||||
window.addEventListener('auth:token-expired', handleTokenExpired)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('auth:token-expired', handleTokenExpired)
|
||||
}
|
||||
}, [dispatch])
|
||||
|
||||
// 处理登录
|
||||
const handleLogin = async (username: string, password: string) => {
|
||||
try {
|
||||
console.log('🔐 尝试登录:', username)
|
||||
setLoginError(null)
|
||||
dispatch(clearError())
|
||||
|
||||
const result = await dispatch(login({ username, password }))
|
||||
|
||||
if (login.fulfilled.match(result)) {
|
||||
console.log('✅ 登录成功')
|
||||
setLoginError(null)
|
||||
} else if (login.rejected.match(result)) {
|
||||
const errorMessage = result.payload || '登录失败'
|
||||
console.log('❌ 登录失败:', errorMessage)
|
||||
setLoginError(errorMessage)
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('登录过程出错:', error)
|
||||
throw error // 重新抛出错误,让LoginPage显示加载状态
|
||||
}
|
||||
}
|
||||
|
||||
// 清除登录错误
|
||||
useEffect(() => {
|
||||
if (authError && authError !== loginError) {
|
||||
setLoginError(authError)
|
||||
}
|
||||
}, [authError, loginError])
|
||||
|
||||
// 处理安装完成
|
||||
const handleInstallComplete = () => {
|
||||
console.log('🔐 安装完成,刷新初始化状态')
|
||||
setSystemInitialized(true)
|
||||
setLoginError(null)
|
||||
}
|
||||
|
||||
// 初始化加载中
|
||||
if (isInitializing || systemInitialized === null) {
|
||||
return (
|
||||
<div style={{
|
||||
minHeight: '100vh',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
|
||||
}}>
|
||||
<Spin size="large" style={{ color: 'white' }} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 系统未初始化,显示安装页面
|
||||
if (!systemInitialized) {
|
||||
return (
|
||||
<InstallPage onInstallComplete={handleInstallComplete} />
|
||||
)
|
||||
}
|
||||
|
||||
// 系统已初始化但未登录,显示登录页面
|
||||
if (!isAuthenticated) {
|
||||
return (
|
||||
<LoginPage
|
||||
onLogin={handleLogin}
|
||||
loading={authLoading}
|
||||
error={loginError || undefined}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// 已登录,显示主应用
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
export default AuthGuard
|
||||
Reference in New Issue
Block a user