生产管理系统 - 未登录拦截 客户端中间件开发

This commit is contained in:
2025-10-31 17:34:23 +08:00
parent 0df19c9cfb
commit a1b3335664
3 changed files with 80 additions and 77 deletions

View File

@@ -2,6 +2,7 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { AuthProvider } from '@/components/auth/AuthContext'; import { AuthProvider } from '@/components/auth/AuthContext';
import { ClientAuthInterceptor } from '@/components/auth/ClientAuthInterceptor';
export default function AppLayout({ export default function AppLayout({
children, children,
@@ -14,10 +15,12 @@ export default function AppLayout({
}, []); }, []);
return ( return (
<html lang="en" suppressHydrationWarning> <html lang="zh-CN" suppressHydrationWarning>
<body suppressHydrationWarning> <body suppressHydrationWarning>
<AuthProvider> <AuthProvider>
{children} <ClientAuthInterceptor>
{children}
</ClientAuthInterceptor>
</AuthProvider> </AuthProvider>
</body> </body>
</html> </html>

View File

@@ -0,0 +1,75 @@
'use client';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from './AuthContext';
interface ClientAuthInterceptorProps {
children: React.ReactNode;
}
export function ClientAuthInterceptor({ children }: ClientAuthInterceptorProps) {
const { user, isAuthenticated, loading } = useAuth();
const router = useRouter();
useEffect(() => {
// 如果正在加载,等待加载完成
if (loading) {
return;
}
const currentPath = window.location.pathname;
// 如果已经在登录页面,不需要重定向
if (currentPath === '/login' || currentPath === '/register') {
console.log(`📄 已在认证页面,跳过拦截: ${currentPath}`);
return;
}
// 如果未认证,重定向到登录页
if (!isAuthenticated) {
console.log(`🔒 客户端拦截:未认证用户访问 ${currentPath},重定向到登录页`);
// 保存当前路径,登录后可以跳转回来
const loginUrl = `/login${currentPath !== '/' ? `?redirect=${encodeURIComponent(currentPath)}` : ''}`;
router.push(loginUrl);
return;
}
console.log(`✅ 客户端认证通过:用户 ${user?.username} 访问 ${window.location.pathname}`);
}, [isAuthenticated, loading, user, router]);
const currentPath = typeof window !== 'undefined' ? window.location.pathname : '';
// 如果正在加载,显示加载状态
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="text-center">
<div className="w-8 h-8 border-2 border-blue-500 border-t-transparent rounded-full animate-spin mb-4"></div>
<p className="text-gray-600">...</p>
</div>
</div>
);
}
// 如果在认证页面(登录/注册),直接渲染子组件,不需要认证检查
if (currentPath === '/login' || currentPath === '/register') {
return <>{children}</>;
}
// 如果未认证且不在认证页面,显示跳转状态
if (!isAuthenticated) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="text-center">
<div className="w-8 h-8 border-2 border-orange-500 border-t-transparent rounded-full animate-spin mb-4"></div>
<p className="text-gray-600">...</p>
</div>
</div>
);
}
// 认证通过,渲染子组件
return <>{children}</>;
}

View File

@@ -1,75 +0,0 @@
import { NextResponse } from "next/server";
// 定义不需要认证的路径
const publicPaths = [
"/login",
"/register",
"/api",
"/_next",
"/favicon.ico",
"/static"
];
export async function middleware(request: any) {
const { pathname } = request.nextUrl;
console.log(`🔍 中间件拦截路径: ${pathname}`);
// 检查是否是公开路径
if (publicPaths.some(path => pathname.startsWith(path))) {
console.log(`✅ 公开路径,允许访问: ${pathname}`);
return NextResponse.next();
}
try {
// 从 cookie 中获取 token
const token = request.cookies.get("auth-token")?.value;
console.log(`🍪 Cookie中的token状态: ${token ? '存在' : '不存在'}`);
// 如果没有 token重定向到登录页
if (!token) {
console.log(`🔒 未找到认证 token重定向到登录页: ${pathname}`);
const loginUrl = new URL("/login", request.url);
loginUrl.searchParams.set("redirect", pathname);
return NextResponse.redirect(loginUrl);
}
// 简单验证 token 格式不调用API避免复杂依赖
if (token.length < 10) {
console.log(`❌ Token 格式无效,重定向到登录页: ${pathname}`);
const loginUrl = new URL("/login", request.url);
loginUrl.searchParams.set("redirect", pathname);
return NextResponse.redirect(loginUrl);
}
console.log(`✅ Token 存在,允许访问: ${pathname}`);
// 创建响应并添加用户信息标记
const response = NextResponse.next();
response.headers.set('x-middleware-auth', 'validated');
return response;
} catch (error) {
console.error(`❌ 中间件处理失败,重定向到登录页: ${pathname}`, error);
// 发生错误时,重定向到登录页
const loginUrl = new URL("/login", request.url);
loginUrl.searchParams.set("redirect", pathname);
return NextResponse.redirect(loginUrl);
}
}
export const config = {
matcher: [
/*
* 匹配所有路径除了:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* - login 和 register 页面
*/
"/((?!api|_next/static|_next/image|favicon.ico|login|register).*)",
],
};