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

如何在React Admin登录流程中实现2FA等身份认证挑战环节?

实现方案

核心逻辑

  • 用户名密码校验通过后,不直接下发正式鉴权token,仅返回用于2FA校验的临时凭证,同时authProvider的login方法正常resolve并携带challenge: '2FA'标识
  • 改造authProvider.checkAuth逻辑,增加中间态校验:仅存在2FA临时凭证、无正式token时直接返回校验失败,指定跳转路径为2FA验证页,从根本上拦截未完成2FA的用户访问业务资源
  • 2FA验证通过后再下发正式token,替换临时凭证,后续所有鉴权校验正常通过

代码示例

1. 改造authProvider

const authProvider = {
  login: async (credentials) => {
    const { username, password, code } = credentials;
    // 2FA验证码提交场景
    if (code) {
      const verifyRes = await fetch('/api/2fa/verify', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          tempToken: localStorage.getItem('2fa_temp_token'),
          code
        })
      });
      if (verifyRes.ok) {
        const { token } = await verifyRes.json();
        localStorage.removeItem('2fa_temp_token');
        localStorage.setItem('auth_token', token);
        return Promise.resolve();
      }
      return Promise.reject(new Error('验证码无效或已过期'));
    }

    // 用户名密码登录场景
    const loginRes = await fetch('/api/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, password })
    });
    if (loginRes.ok) {
      const data = await loginRes.json();
      // 需要2FA验证的场景
      if (data.require2FA) {
        localStorage.setItem('2fa_temp_token', data.tempToken);
        return Promise.resolve({ challenge: '2FA' });
      }
      // 无需2FA,直接登录
      localStorage.setItem('auth_token', data.token);
      return Promise.resolve();
    }
    return Promise.reject(new Error('用户名或密码错误'));
  },

  checkAuth: async () => {
    const formalToken = localStorage.getItem('auth_token');
    const tempToken = localStorage.getItem('2fa_temp_token');
    // 仅存在2FA临时凭证,未完成验证
    if (tempToken && !formalToken) {
      return Promise.reject({ redirectTo: '/2fa' });
    }
    // 无任何凭证,未登录
    if (!formalToken) {
      return Promise.reject();
    }
    return Promise.resolve();
  },

  logout: () => {
    localStorage.removeItem('auth_token');
    localStorage.removeItem('2fa_temp_token');
    return Promise.resolve();
  },
  // 其余getPermissions、getIdentity等方法按原有逻辑实现即可
};

2. 自定义登录页逻辑

import { useLogin, useNotify } from 'react-admin';
import { useNavigate } from 'react-router-dom';

const CustomLoginPage = () => {
  const login = useLogin();
  const notify = useNotify();
  const navigate = useNavigate();

  const handleLoginSubmit = async (values) => {
    try {
      const loginResult = await login({
        username: values.username,
        password: values.password
      });
      // 需要2FA验证,跳转对应页面
      if (loginResult?.challenge === '2FA') {
        navigate('/2fa');
        return;
      }
      // 无需2FA,直接跳转后台首页
      navigate('/dashboard');
    } catch (err) {
      notify(err.message, { type: 'error' });
    }
  };

  // 表单渲染逻辑省略
};

3. 2FA验证页逻辑

import { useLogin, useNotify } from 'react-admin';
import { useNavigate } from 'react-router-dom';

const TwoFAPage = () => {
  const login = useLogin();
  const notify = useNotify();
  const navigate = useNavigate();

  const handleCodeSubmit = async (code) => {
    try {
      await login({ code });
      navigate('/dashboard');
    } catch (err) {
      notify(err.message, { type: 'error' });
    }
  };

  // 验证码表单渲染逻辑省略
};

注意事项

  • 需要在React Admin的自定义路由中注册2FA页面,确保路径可以正常访问
  • 该方案完全兼容React Admin的原生鉴权逻辑,无需修改底层源码,也适用于强制修改密码、邮箱验证等其他需要中间校验状态的场景

内容的提问来源于stack exchange,提问作者Jeremy Richardson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 13:15:02