如何在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
相关产品推荐
相关产品推荐

