You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 12:35:11