NextJS如何实现未认证用户无页面闪烁重定向?
解决未认证用户访问管理员控制台的页面闪烁问题
我已经完成页面的管理员权限认证配置,权限路由保护功能正常(采用基于Token的认证而非Cookie认证),但目前存在一个问题:未认证用户访问管理员控制台时,页面会先短暂显示内容再重定向,希望避免这种闪烁现象。
当前使用的代码
import { useRouter } from "next/router" import { useState, useEffect } from "react"; import useSWR from 'swr'; import axios from "../lib/axios"; export const useAuth = ({ middleware } = {}) => { const router = useRouter(); // LOADING const [isLoading, setisLoading] = useState(true); // USER const { data: user, error, mutate } = useSWR('/api/user', () => axios .get('/api/user') .then(res => res.data) .catch(error => { if (error.response.status !== 401) throw error router.push('/') }), ) // CSRF const csrf = () => axios.get('/sanctum/csrf-cookie'); // 我用的是Token认证,不知道怎么修改这个 // LOGOUT const logout = async () => { await axios.post('/api/logout'); mutate(null); router.push("/login"); } useEffect(() => { if (user || error) { setisLoading(false); } if (middleware == "guest" && user) router.push("/"); if (middleware == "auth" && error) router.push("/login"); }) return { user, csrf, logout, isLoading } }
尝试过的无效代码
useEffect(() => { axios.get(`/api/user`).then(res => { if (res.status === 200) { console.log('nice'); } setLoading(false); }); }, []); axios.interceptors.response.use(undefined, function axiosRetryInterceptor(err) { if (err.response.status === 401) { router.push('/'); } return Promise.reject(err); });
可行解决方案
页面闪烁的核心原因是:认证状态未确认前就渲染了管理员页面内容,后续才触发重定向。解决思路是在认证状态明确前,不渲染页面核心内容,仅展示加载状态,同时优化认证逻辑的执行时机。
1. 修复useAuth钩子的核心问题
import { useRouter } from "next/router" import { useState, useEffect } from "react"; import useSWR from 'swr'; import axios from "../lib/axios"; export const useAuth = ({ middleware } = {}) => { const router = useRouter(); const [isLoading, setIsLoading] = useState(true); // 用SWR获取用户信息,仅抛出错误由SWR统一管理状态 const { data: user, error, mutate } = useSWR('/api/user', async () => { try { const res = await axios.get('/api/user'); return res.data; } catch (err) { // 直接抛出错误,让SWR捕获error状态 throw err; } }); // 移除无用的CSRF方法(Token认证无需Sanctum CSRF Cookie) // LOGOUT const logout = async () => { await axios.post('/api/logout'); mutate(null); router.push("/login"); } // 给useEffect添加依赖项,避免无限触发;仅在状态明确后关闭加载态 useEffect(() => { if (user || error) { setIsLoading(false); } // 根据中间件类型处理路由跳转 if (middleware === "guest" && user) { router.push("/"); } if (middleware === "auth" && error) { // 仅在401未认证时跳转登录页 if (error.response?.status === 401) { router.push("/login"); } } }, [user, error, middleware, router]); // 必须添加依赖项 return { user, logout, isLoading } }
2. 配置Axios自动携带Token(关键)
在你的../lib/axios文件中添加请求拦截器,确保每次请求自动带上认证Token(假设Token存在localStorage中):
import axios from 'axios'; const axiosInstance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL // 替换为你的API地址 }); // 请求拦截器:自动在请求头添加Token axiosInstance.interceptors.request.use((config) => { const token = localStorage.getItem('auth_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); export default axiosInstance;
3. 管理员页面组件中控制渲染逻辑
在管理员控制台页面,先判断isLoading状态,加载完成前仅展示加载占位(如空白、骨架屏),不渲染核心内容:
import { useAuth } from '../hooks/useAuth'; export default function AdminDashboard() { const { user, isLoading } = useAuth({ middleware: 'auth' }); // 加载状态下仅展示加载提示,不渲染管理员内容 if (isLoading) { return <div>加载中...</div>; // 可替换为骨架屏组件 } // 加载完成后渲染管理员页面内容 return ( <div> <h1>管理员控制台</h1> {/* 其他页面内容 */} </div> ); }
关键说明
- 移除了无用的CSRF方法:Token认证模式下不需要Sanctum的CSRF Cookie请求,直接删除即可。
- 修复
useEffect依赖:之前的useEffect没有依赖项会导致无限触发,添加依赖后仅在状态变化时执行逻辑。 - 延迟页面渲染:通过
isLoading状态控制,确保认证结果返回前不渲染敏感内容,从根源避免闪烁。
内容的提问来源于stack exchange,提问作者Babaduk98
相关产品推荐
相关产品推荐

