fix(auth): handle zero-permission users gracefully and prevent redirect loops

This commit is contained in:
2026-08-11 07:36:01 +08:00
parent 04c12ad340
commit 2e02eee66c
6 changed files with 84 additions and 7 deletions
+11 -2
View File
@@ -2,6 +2,7 @@ import { Link, Outlet, createFileRoute, redirect, useNavigate } from '@tanstack/
import { useQuery } from '@tanstack/react-query'
import { useEffect } from 'react'
import { LogOut, Moon, Sun } from 'lucide-react'
import { toast } from 'sonner'
import { fetchMe } from '@/api/modules/auth'
import { can, isAuthenticated, useAuthStore } from '@/auth/store'
import { NAV_GROUPS } from '@/components/layout/nav'
@@ -41,11 +42,19 @@ function AuthedLayout() {
useEffect(() => {
if (me.data) {
const roles = me.data.meta?.roles ?? []
const permissions = me.data.meta?.permissions ?? []
if (roles.length === 0 && permissions.length === 0) {
useAuthStore.getState().clear()
toast.error('账号未分配管理后台权限,已退出登录')
void navigate({ to: '/login' })
return
}
useAuthStore
.getState()
.setSession(me.data.data, me.data.meta?.roles ?? [], me.data.meta?.permissions ?? [])
.setSession(me.data.data, roles, permissions)
}
}, [me.data])
}, [me.data, navigate])
useEffect(() => {
if (me.isError && !isAuthenticated()) {
+46 -2
View File
@@ -1,7 +1,51 @@
import { createFileRoute, redirect } from '@tanstack/react-router'
import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router'
import { ShieldAlert, LogOut } from 'lucide-react'
import { getFirstAccessibleRoute } from '@/components/layout/nav'
import { useAuthStore } from '@/auth/store'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
export const Route = createFileRoute('/_authed/')({
beforeLoad: () => {
throw redirect({ to: '/categories' })
const target = getFirstAccessibleRoute()
if (target && target !== '/') {
throw redirect({ to: target })
}
},
component: DashboardIndexPage,
})
function DashboardIndexPage() {
const navigate = useNavigate()
const user = useAuthStore((s) => s.user)
const logout = () => {
useAuthStore.getState().clear()
void navigate({ to: '/login' })
}
return (
<div className="flex min-h-[60vh] items-center justify-center p-4">
<Card className="w-full max-w-md text-center shadow-xs">
<CardHeader>
<div className="mx-auto mb-2 flex size-12 items-center justify-center rounded-full bg-amber-500/10 text-amber-600 dark:bg-amber-500/20 dark:text-amber-400">
<ShieldAlert className="size-6" />
</div>
<CardTitle className="text-xl"></CardTitle>
<CardDescription>
{user?.display_name ?? user?.username ?? user?.uid}
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-xs text-muted-foreground">
访
</p>
<Button variant="outline" onClick={logout} className="gap-2">
<LogOut className="size-4" />
退
</Button>
</CardContent>
</Card>
</div>
)
}
+7 -1
View File
@@ -45,7 +45,13 @@ function LoginPage() {
const me = await fetchMe()
const roles = me.meta?.roles ?? []
useAuthStore.getState().setSession(me.data, roles, me.meta?.permissions ?? [])
const permissions = me.meta?.permissions ?? []
if (roles.length === 0 && permissions.length === 0) {
useAuthStore.getState().clear()
toast.error('该账号未分配管理后台权限,无法登录')
return
}
useAuthStore.getState().setSession(me.data, roles, permissions)
await navigate({ to: '/' })
} catch (error) {