React + Laravel Sanctum页面刷新时保持登录状态的实现方案
页面刷新时保持Laravel Sanctum + React Context登录状态的实现方案
问题描述
我正在开发一个包含登录功能的项目,前端使用React,后端使用Laravel,认证采用Laravel Sanctum,状态管理使用Context API。请问如何实现页面刷新时保持用户登录状态?
我的相关代码
Laravel登录函数
public function login(Request $request) { $fields = $request->validate([ 'email' => 'required|string', 'password' => 'required|string' ]); // 用输入的邮箱获取用户 $user = User::where('email', $fields['email'])->first(); // 检查用户是否存在,以及输入密码与数据库密码是否匹配 if ($user && Hash::check($fields['password'], $user->password)) { Auth::login($user); } else { return response([ 'message' => 'Wrong credentials' ], 401); } $token = $user->createToken('myapptoken')->plainTextToken; $response = [ 'user' => $user, 'token' => $token, 'message' => 'logged in', ]; return response($response); }
React登录函数
const { loginUser } = useAuth(); const emailRef = useRef(""); const passwordRef = useRef(""); const cookies = new Cookies(); const handleSubmit = (e) => { e.preventDefault(); const email = emailRef.current.value; const password = passwordRef.current.value; const userData = { email, password, }; loginUser(userData); // 重置输入值 emailRef.current.value = ""; passwordRef.current.value = ""; };
Context API登录处理函数
const loginMutation = useMutation(login, { onSuccess: (data) => { setAccessToken(data.data.token); const token = data.data.token; setUserId(data.data.user.id); setUserName(data.data.user.name); setIsLoggedIn(true); console.log(token); localStorage.setItem("accessToken", token); localStorage.setItem("isLoggedIn", true); // localStorage.setItem('isLoggedIn', true); }, onError: (error) => { console.error("errore duarente il login:", error.response.data); }, }); const loginUser = async (userData) => { try { await loginMutation.mutateAsync(userData); } catch (error) { console.error("errore durante il login"); } };
解决方案
要实现页面刷新后保持登录状态,核心是在应用初始化时从localStorage读取保存的令牌,验证其有效性后同步到Context状态中。具体步骤如下:
1. 确认Laravel后端路由配置
确保/api/user路由受auth:sanctum中间件保护,用于验证令牌并返回用户信息:
// routes/api.php Route::middleware('auth:sanctum')->get('/user', function (Request $request) { return $request->user(); });
2. 修改React Context添加初始化逻辑
在你的Context Provider中添加初始化钩子,在组件挂载时检查本地存储的令牌并验证:
import { useEffect, useState, createContext, useContext } from 'react'; import { useMutation } from '@tanstack/react-query'; // 新增:获取当前登录用户信息的函数 const fetchCurrentUser = async (token) => { const res = await fetch('/api/user', { headers: { 'Authorization': `Bearer ${token}` } }); if (!res.ok) throw new Error('令牌无效'); return res.json(); }; const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [accessToken, setAccessToken] = useState(null); const [userId, setUserId] = useState(null); const [userName, setUserName] = useState(null); const [isLoggedIn, setIsLoggedIn] = useState(false); // 保留原有的登录Mutation const loginMutation = useMutation(login, { onSuccess: (data) => { setAccessToken(data.data.token); setUserId(data.data.user.id); setUserName(data.data.user.name); setIsLoggedIn(true); localStorage.setItem("accessToken", data.data.token); localStorage.setItem("isLoggedIn", "true"); // 注意存为字符串 }, onError: (error) => { console.error("登录出错:", error.response?.data || error.message); }, }); // 初始化:读取本地存储并验证令牌 useEffect(() => { const savedToken = localStorage.getItem("accessToken"); const savedIsLoggedIn = localStorage.getItem("isLoggedIn") === "true"; if (savedToken && savedIsLoggedIn) { fetchCurrentUser(savedToken) .then(user => { setAccessToken(savedToken); setUserId(user.id); setUserName(user.name); setIsLoggedIn(true); }) .catch(() => { // 令牌无效,清除本地存储并重置状态 localStorage.removeItem("accessToken"); localStorage.removeItem("isLoggedIn"); setIsLoggedIn(false); }); } }, []); // 新增:登出函数 const logoutUser = () => { setAccessToken(null); setUserId(null); setUserName(null); setIsLoggedIn(false); localStorage.removeItem("accessToken"); localStorage.removeItem("isLoggedIn"); // 可选:调用后端注销令牌接口 fetch('/api/logout', { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}` } }); }; const loginUser = async (userData) => { try { await loginMutation.mutateAsync(userData); } catch (error) { console.error("登录失败"); } }; return ( <AuthContext.Provider value={{ loginUser, logoutUser, accessToken, userId, userName, isLoggedIn }}> {children} </AuthContext.Provider> ); }; export const useAuth = () => useContext(AuthContext);
3. 关键要点
- 本地存储读取:页面刷新后,从
localStorage获取之前保存的令牌和登录状态。 - 令牌验证:通过
/api/user接口验证令牌是否有效,避免使用过期或无效令牌。 - 状态同步:验证成功后将用户信息同步到Context状态,失败则清除本地存储并重置登录状态。
- 登出处理:添加登出函数,同时清除Context状态和本地存储的数据,确保状态一致。
这样就能实现页面刷新后自动恢复用户登录状态,同时保证令牌的有效性。
内容的提问来源于stack exchange,提问作者FrankSDA
相关产品推荐
相关产品推荐

