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

React中SetInterval的应用及无刷新登录注册注销认证方案

React无刷新认证实现方案

无刷新认证的核心是前端维护全局认证状态,通过异步API完成身份交互,配合路由控制页面访问权限,全程无需整页刷新。以下是具体实现方案:

一、全局认证状态管理

首先要在前端统一管理用户的认证状态(是否登录、用户信息、令牌等),常用两种方案:

1. React Context + useReducer(中小型项目首选)

通过Context全局共享认证状态,useReducer处理状态变更逻辑:

// AuthContext.js
import { createContext, useContext, useReducer, useEffect } from 'react';

const AuthContext = createContext();

const authReducer = (state, action) => {
  switch (action.type) {
    case 'LOGIN_SUCCESS':
      localStorage.setItem('token', action.payload.token);
      localStorage.setItem('refreshToken', action.payload.refreshToken);
      return { ...state, user: action.payload.user, isAuthenticated: true };
    case 'LOGOUT':
      localStorage.removeItem('token');
      localStorage.removeItem('refreshToken');
      return { ...state, user: null, isAuthenticated: false };
    case 'REGISTER_SUCCESS':
      return { ...state, message: action.payload.message };
    default:
      return state;
  }
};

export const AuthProvider = ({ children }) => {
  const [state, dispatch] = useReducer(authReducer, {
    user: null,
    isAuthenticated: false,
    message: ''
  });

  // 初始化时从本地存储恢复登录状态
  useEffect(() => {
    const token = localStorage.getItem('token');
    if (token) {
      // 调用API验证token有效性,有效则同步用户信息
      fetch('/api/user/profile', {
        headers: { Authorization: `Bearer ${token}` }
      })
      .then(res => res.json())
      .then(user => dispatch({ type: 'LOGIN_SUCCESS', payload: { user, token } }))
      .catch(() => localStorage.removeItem('token'));
    }
  }, []);

  // 监听多标签页状态同步
  useEffect(() => {
    const handleStorageChange = (e) => {
      if (e.key === 'token' && !e.newValue) {
        dispatch({ type: 'LOGOUT' });
      }
    };
    window.addEventListener('storage', handleStorageChange);
    return () => window.removeEventListener('storage', handleStorageChange);
  }, [dispatch]);

  return (
    <AuthContext.Provider value={{ state, dispatch }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

2. Redux Toolkit(大型项目)

对于复杂项目,用Redux Toolkit管理状态更规范,通过createAsyncThunk处理异步登录/注册请求,createSlice维护状态逻辑,核心思路和Context方案一致,只是状态管理的载体不同。

二、登录/注册/注销的异步交互

所有身份操作通过API异步完成,请求成功后更新前端状态:

1. 登录操作

// Login.jsx
import { useState } from 'react';
import { useAuth } from './AuthContext';
import { useNavigate } from 'react-router-dom';

const Login = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const { dispatch } = useAuth();
  const navigate = useNavigate();

  const handleLogin = async (e) => {
    e.preventDefault();
    try {
      const res = await fetch('/api/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password })
      });
      const data = await res.json();
      if (res.ok) {
        dispatch({ type: 'LOGIN_SUCCESS', payload: data });
        navigate('/dashboard'); // 登录成功跳转至权限页
      } else {
        alert(data.message || '登录失败');
      }
    } catch (err) {
      alert('网络异常,请稍后重试');
    }
  };

  return (
    <form onSubmit={handleLogin}>
      <input 
        type="email" 
        value={email} 
        onChange={(e) => setEmail(e.target.value)} 
        placeholder="邮箱" 
        required 
      />
      <input 
        type="password" 
        value={password} 
        onChange={(e) => setPassword(e.target.value)} 
        placeholder="密码" 
        required 
      />
      <button type="submit">登录</button>
    </form>
  );
};

2. 注册操作

注册成功后可选择自动登录或引导至登录页:

// Register.jsx
import { useState } from 'react';
import { useAuth } from './AuthContext';
import { useNavigate } from 'react-router-dom';

const Register = () => {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const { dispatch } = useAuth();
  const navigate = useNavigate();

  const handleRegister = async (e) => {
    e.preventDefault();
    try {
      const res = await fetch('/api/auth/register', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name, email, password })
      });
      const data = await res.json();
      if (res.ok) {
        // 注册成功自动登录
        dispatch({ type: 'LOGIN_SUCCESS', payload: data });
        navigate('/dashboard');
        // 若需手动登录则跳转至登录页:navigate('/login')
      } else {
        alert(data.message || '注册失败');
      }
    } catch (err) {
      alert('网络异常,请稍后重试');
    }
  };

  return (
    <form onSubmit={handleRegister}>
      <input 
        type="text" 
        value={name} 
        onChange={(e) => setName(e.target.value)} 
        placeholder="用户名" 
        required 
      />
      <input 
        type="email" 
        value={email} 
        onChange={(e) => setEmail(e.target.value)} 
        placeholder="邮箱" 
        required 
      />
      <input 
        type="password" 
        value={password} 
        onChange={(e) => setPassword(e.target.value)} 
        placeholder="密码" 
        required 
      />
      <button type="submit">注册</button>
    </form>
  );
};

3. 注销操作

清除本地令牌,更新认证状态后跳转至登录页:

// Navbar.jsx
import { useAuth } from './AuthContext';
import { useNavigate } from 'react-router-dom';

const Navbar = () => {
  const { state, dispatch } = useAuth();
  const navigate = useNavigate();

  const handleLogout = () => {
    dispatch({ type: 'LOGOUT' });
    navigate('/login');
  };

  return (
    <nav>
      {state.isAuthenticated ? (
        <>
          <span>欢迎,{state.user.name}</span>
          <button onClick={handleLogout}>注销</button>
        </>
      ) : (
        <>
          <a href="/login">登录</a>
          <a href="/register">注册</a>
        </>
      )}
    </nav>
  );
};

三、路由守卫(保护权限页面)

通过自定义组件控制未认证用户无法访问需要权限的页面:

// ProtectedRoute.jsx
import { Navigate } from 'react-router-dom';
import { useAuth } from './AuthContext';

const ProtectedRoute = ({ children }) => {
  const { state } = useAuth();
  if (!state.isAuthenticated) {
    // 未登录跳转至登录页
    return <Navigate to="/login" replace />;
  }
  return children;
};

// 路由配置中使用
<Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />

四、令牌自动刷新(可选)

若使用JWT令牌,为避免token过期后用户重新登录,可封装带自动刷新逻辑的请求工具:

// api.js
const fetchWithAuth = async (url, options = {}) => {
  const token = localStorage.getItem('token');
  const headers = {
    'Content-Type': 'application/json',
    ...options.headers,
    ...(token && { Authorization: `Bearer ${token}` })
  };

  let res = await fetch(url, { ...options, headers });

  // 401状态码表示token过期,尝试刷新
  if (res.status === 401) {
    const refreshToken = localStorage.getItem('refreshToken');
    if (refreshToken) {
      const refreshRes = await fetch('/api/auth/refresh-token', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ refreshToken })
      });
      const refreshData = await refreshRes.json();
      if (refreshRes.ok) {
        localStorage.setItem('token', refreshData.token);
        // 重新发起原请求
        res = await fetch(url, { ...options, headers: { ...headers, Authorization: `Bearer ${refreshData.token}` } });
      } else {
        // 刷新失败,强制注销
        localStorage.removeItem('token');
        localStorage.removeItem('refreshToken');
        window.location.href = '/login';
      }
    } else {
      window.location.href = '/login';
    }
  }

  return res;
};

// 使用示例
const getUserProfile = async () => {
  const res = await fetchWithAuth('/api/user/profile');
  return res.json();
};

五、安全注意事项

  • 令牌存储:优先使用httpOnly cookie存储令牌,避免XSS攻击;若必须存在前端,需防范XSS风险
  • 通信安全:所有API请求需开启HTTPS,防止令牌被窃听
  • 权限校验:前端路由守卫仅做辅助,后端必须对每个权限接口做令牌有效性校验

内容的提问来源于stack exchange,提问作者Vasim Shaikh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 04:35:25