diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts index 3974847..3b5d174 100644 --- a/src/app/api/login/route.ts +++ b/src/app/api/login/route.ts @@ -24,41 +24,6 @@ export async function POST(req: NextRequest) { ); } - // 登录成功:写入 HttpOnly Cookie - const res = NextResponse.json({ ok: true }); - res.cookies.set({ - name: 'password', - value: password, - httpOnly: true, - sameSite: 'lax', - secure: process.env.NODE_ENV === 'production', - maxAge: 60 * 60 * 24 * 30, // 30 天 - path: '/', - }); - - return res; - } catch (error) { - return NextResponse.json({ error: '服务器错误' }, { status: 500 }); - } -} - -// 使用 Cookie 校验登录状态 -export async function GET(req: NextRequest) { - try { - const result = process.env.PASSWORD; - - // 未设置 PASSWORD 则直接放行 - if (!result) { - return NextResponse.json({ ok: true }); - } - - const cookiePassword = req.cookies.get('password')?.value; - const matched = cookiePassword === result; - - if (!matched) { - return NextResponse.json({ ok: false }, { status: 401 }); - } - return NextResponse.json({ ok: true }); } catch (error) { return NextResponse.json({ error: '服务器错误' }, { status: 500 }); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 04f7d3b..ef82afc 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,6 +3,7 @@ import { Inter } from 'next/font/google'; import './globals.css'; +import AuthProvider from '../components/AuthProvider'; import { ThemeProvider } from '../components/ThemeProvider'; const inter = Inter({ subsets: ['latin'] }); @@ -37,7 +38,7 @@ export default function RootLayout({ enableSystem disableTransitionOnChange > - {children} + {children} diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 5221c57..75f2fc9 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -27,6 +27,11 @@ function LoginPageClient() { }); if (res.ok) { + // 保存密码以供后续请求使用 + if (typeof window !== 'undefined') { + localStorage.setItem('password', password); + } + const redirect = searchParams.get('redirect') || '/'; router.replace(redirect); } else if (res.status === 401) { diff --git a/src/components/AuthProvider.tsx b/src/components/AuthProvider.tsx new file mode 100644 index 0000000..590ac9b --- /dev/null +++ b/src/components/AuthProvider.tsx @@ -0,0 +1,46 @@ +'use client'; + +import { usePathname, useRouter } from 'next/navigation'; +import { useEffect } from 'react'; + +interface Props { + children: React.ReactNode; +} + +export default function AuthProvider({ children }: Props) { + const router = useRouter(); + const pathname = usePathname(); + + useEffect(() => { + // 登录页或 API 路径不做校验,避免死循环 + if (pathname.startsWith('/login')) return; + + const password = localStorage.getItem('password'); + const fullPath = + typeof window !== 'undefined' + ? window.location.pathname + window.location.search + : pathname; + + // 有密码时验证 + (async () => { + try { + const res = await fetch('/api/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ password }), + }); + + if (!res.ok) { + // 校验未通过,清理并跳转登录 + localStorage.removeItem('password'); + router.replace(`/login?redirect=${encodeURIComponent(fullPath)}`); + } + } catch (error) { + // 网络错误等也认为未登录 + router.replace(`/login?redirect=${encodeURIComponent(fullPath)}`); + } + })(); + }, [pathname, router]); + + return <>{children}; +} diff --git a/src/middleware.ts b/src/middleware.ts deleted file mode 100644 index cef28f6..0000000 --- a/src/middleware.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; - -// 全站(含 /api)鉴权中间件,运行于 Edge Runtime。 -export async function middleware(req: NextRequest) { - const { pathname, search } = req.nextUrl; - - // 1. 放行无需鉴权的路径 - if ( - pathname.startsWith('/login') || // 登录页 - pathname.startsWith('/api/login') || // 登录接口 - pathname.startsWith('/_next') || // Next.js 静态文件 - pathname === '/favicon.ico' || - pathname.startsWith('/icons') || - pathname === '/manifest.json' || - pathname === '/logo.png' || - pathname === '/screenshot.png' - ) { - return NextResponse.next(); - } - - // 通过后端接口验证登录状态(GET /api/login) - const origin = req.nextUrl.origin; - const verifyRes = await fetch(`${origin}/api/login`, { - method: 'GET', - headers: { - Cookie: req.headers.get('cookie') || '', - }, - }); - - if (verifyRes.ok) { - return NextResponse.next(); - } - - // 未通过校验:API 返回 401,页面跳转登录 - if (pathname.startsWith('/api')) { - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); - } - - const loginUrl = req.nextUrl.clone(); - loginUrl.pathname = '/login'; - loginUrl.searchParams.set('redirect', pathname + search); - return NextResponse.redirect(loginUrl); -} - -// 2. 指定哪些路径使用 middleware -export const config = { - matcher: [ - '/((?!_next/static|_next/image|favicon.ico|manifest.json|icons|logo.png|screenshot.png).*)', - ], -};