基于React+Laravel Sanctum实现Token过期自动登出方案咨询
Laravel Sanctum Token过期自动登出的实现位置与方案
你的项目用Laravel Sanctum生成1分钟过期的token,想要实现token过期时自动登出并跳转登录页,自动登出逻辑需要分两部分实现,分别放在RequireAuth.jsx和axios拦截器文件中,Login.jsx只负责登录时的状态存储,不适合放全局过期处理逻辑,具体方案如下:
一、先补充后端返回token过期时间
首先需要让后端把token的过期时间一起返回给前端,修改你的AuthController代码:
// AuthController 生成token并返回过期时间 $tokenResult = $user->createToken('remember_token', expiresAt:now()->addMinute()); $remember_token = $tokenResult->plainTextToken; // 获取token过期时间 $expires_at = $tokenResult->accessToken->expires_at; // 返回数据时带上expires_at return response()->json([ 'remember_token' => $remember_token, 'user' => $user, 'roles_name' => $roles_name, // 保留你的原有逻辑 'role_id' => $user->role_id, 'expires_at' => $expires_at ]);
二、Login.jsx的修改(仅存储状态)
在登录成功时,把后端返回的expires_at存入auth状态,不需要在这里处理过期逻辑:
// Login.jsx 的onSubmit函数中 const { remember_token, user, roles_name, role_id, expires_at } = response.data; // 把过期时间存入auth状态 setAuth({ user, remember_token, roles_name, role_id, expires_at });
三、RequireAuth.jsx:主动检测token过期(路由守卫)
RequireAuth.jsx是路由守卫组件,每次路由切换或定时检查token是否即将过期,主动触发登出,这是预防式的处理:
import { useEffect } from 'react'; import { useAuth } from '../hooks/useAuth'; // 你的useAuth状态管理钩子 import { useNavigate } from 'react-router-dom'; const RequireAuth = ({ children, requiredRole }) => { const { auth, setAuth } = useAuth(); const navigate = useNavigate(); useEffect(() => { const checkTokenExpiry = () => { if (!auth?.expires_at) return; const expiryTime = new Date(auth.expires_at).getTime(); const currentTime = Date.now(); const bufferTime = 5000; // 提前5秒触发登出,避免刚好过期时请求失败 // 检查是否即将过期或已过期 if (currentTime + bufferTime >= expiryTime) { // 执行登出:清空状态、移除本地存储(如果有)、跳转登录页 setAuth({}); localStorage.removeItem('auth'); // 如果你用localStorage存了auth状态 navigate('/login', { replace: true }); } }; // 初始化时检查一次 checkTokenExpiry(); // 定时检查,每10秒检测一次 const interval = setInterval(checkTokenExpiry, 10000); // 组件卸载时清除定时器,避免内存泄漏 return () => clearInterval(interval); }, [auth, setAuth, navigate]); // 保留你原有的路由守卫逻辑,比如检查用户是否登录、角色是否匹配 if (!auth?.user) { navigate('/login', { replace: true }); return null; } if (requiredRole && auth.role_id !== requiredRole) { navigate('/unauthorized', { replace: true }); return null; } return children; }; export default RequireAuth;
四、axios拦截器:被动处理401错误(请求失败时)
创建一个axios实例文件(比如axiosInstance.js),拦截所有响应,当后端返回401(token已过期)时,执行登出逻辑:
import axios from 'axios'; import { useAuth } from '../hooks/useAuth'; import { useNavigate } from 'react-router-dom'; import { createContext, useContext } from 'react'; // 创建Auth上下文,解决axios拦截器无法直接使用React Hooks的问题 const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const { auth, setAuth } = useAuth(); const navigate = useNavigate(); return <AuthContext.Provider value={{ auth, setAuth, navigate }}>{children}</AuthContext.Provider>; }; // 创建axios实例 const axiosInstance = axios.create({ baseURL: process.env.REACT_APP_API_URL, // 替换成你的API地址 }); // 请求拦截器:自动在请求头添加token axiosInstance.interceptors.request.use( (config) => { const { auth } = useContext(AuthContext); if (auth?.remember_token) { config.headers.Authorization = `Bearer ${auth.remember_token}`; } return config; }, (error) => Promise.reject(error) ); // 响应拦截器:处理401错误 axiosInstance.interceptors.response.use( (response) => response, (error) => { const { setAuth, navigate } = useContext(AuthContext); // 当后端返回401(token过期或无效)时,执行登出 if (error.response?.status === 401) { setAuth({}); localStorage.removeItem('auth'); navigate('/login', { replace: true }); } return Promise.reject(error); } ); export default axiosInstance;
为什么不放在Login.jsx?
Login.jsx只负责处理登录提交的一次性逻辑,用户登录后这个组件就会被卸载,无法持续检测token状态或拦截全局请求错误,所以不适合放自动登出逻辑。
内容的提问来源于stack exchange,提问作者Christian Medallada
相关产品推荐
相关产品推荐

