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

如何为使用ASP.NET Core Identity的项目配置微软验证器多因素认证

ASP.NET Core Identity 集成微软验证器多因素认证(MFA)实现方案

ASP.NET Core Identity 本身内置了标准 TOTP(基于时间的一次性密码)协议支持,微软验证器完全兼容该协议,无需引入第三方依赖即可快速落地实现,整体实现分为后端接口开发和前端交互适配两部分:

后端实现步骤(ASP.NET Core)

  • 第一步:在 Program.cs 中配置 Identity 启用 TOTP 令牌提供器
builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
    options.SignIn.RequireConfirmedAccount = true;
    // 指定默认的验证器令牌提供器
    options.Tokens.AuthenticatorTokenProvider = TokenOptions.DefaultAuthenticatorProvider;
})
.AddEntityFrameworkStores<YourCustomDbContext>()
.AddDefaultTokenProviders();
  • 第二步:开发MFA绑定信息生成接口,返回可被微软验证器识别的二维码内容
[Authorize]
[HttpGet("mfa/get-setup-info")]
public async Task<IActionResult> GetMfaSetupInfo()
{
    var currentUser = await _userManager.GetUserAsync(User);
    // 生成用户专属的TOTP密钥
    var rawKey = await _userManager.GetAuthenticatorKeyAsync(currentUser);
    if (string.IsNullOrEmpty(rawKey))
    {
        await _userManager.ResetAuthenticatorKeyAsync(currentUser);
        rawKey = await _userManager.GetAuthenticatorKeyAsync(currentUser);
    }
    // 按照otpauth协议生成二维码内容,微软验证器扫码即可识别绑定
    var qrCodeContent = $"otpauth://totp/你的项目名称:{currentUser.Email}?secret={rawKey}&issuer=你的项目名称&digits=6";
    // 格式化密钥,方便用户无法扫码时手动输入绑定
    var formattedKey = string.Join(" ", rawKey.Chunk(4).Select(c => new string(c)));
    return Ok(new { QrCodeContent = qrCodeContent, ManualKey = formattedKey });
}
  • 第三步:开发MFA绑定验证接口,验证通过后开启用户的多因素认证
[Authorize]
[HttpPost("mfa/enable")]
public async Task<IActionResult> EnableMfa([FromBody] EnableMfaRequest request)
{
    var currentUser = await _userManager.GetUserAsync(User);
    // 清理用户输入的特殊字符,兼容空格、分隔符等输入格式
    var verifyCode = request.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
    var isCodeValid = await _userManager.VerifyTwoFactorTokenAsync(currentUser, 
        _userManager.Options.Tokens.AuthenticatorTokenProvider, verifyCode);
    if (!isCodeValid)
    {
        return BadRequest("验证码无效,请检查微软验证器显示的代码");
    }
    // 开启用户MFA
    await _userManager.SetTwoFactorEnabledAsync(currentUser, true);
    // 可选:生成10个恢复码,用户丢失验证器时可用来登录
    var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(currentUser, 10);
    return Ok(new { Enabled = true, RecoveryCodes = recoveryCodes });
}
  • 第四步:适配登录流程,支持MFA二次校验
[HttpPost("account/login")]
public async Task<IActionResult> Login([FromBody] LoginRequest request)
{
    var user = await _userManager.FindByEmailAsync(request.Email);
    if (user == null || !await _userManager.CheckPasswordAsync(user, request.Password))
    {
        return Unauthorized("账号或密码错误");
    }
    // 判断用户是否开启MFA
    if (await _userManager.GetTwoFactorEnabledAsync(user))
    {
        return Ok(new { RequireMfa = true, UserId = user.Id });
    }
    // 未开启MFA直接返回登录凭证
    var accessToken = GenerateJwtToken(user);
    return Ok(new { AccessToken = accessToken });
}

// MFA二次验证接口
[HttpPost("account/login-mfa")]
public async Task<IActionResult> LoginWithMfa([FromBody] MfaLoginRequest request)
{
    var user = await _userManager.FindByIdAsync(request.UserId);
    var verifyCode = request.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
    var isCodeValid = await _userManager.VerifyTwoFactorTokenAsync(user, 
        _userManager.Options.Tokens.AuthenticatorTokenProvider, verifyCode);
    if (!isCodeValid)
    {
        return BadRequest("验证码无效");
    }
    var accessToken = GenerateJwtToken(user);
    return Ok(new { AccessToken = accessToken });
}

前端实现步骤(React)

  • 安装二维码生成依赖 qrcode.react 用于渲染后端返回的otpauth链接为可扫描的二维码
  • MFA绑定流程:用户进入安全设置页,请求/mfa/get-setup-info接口,渲染二维码和手动输入密钥,提示用户用微软验证器扫码,输入6位验证码提交到/mfa/enable接口完成绑定,绑定成功后提示用户妥善保存恢复码
  • 登录流程适配:调用登录接口如果返回RequireMfa = true,跳转至MFA验证页,用户输入验证码后调用/account/login-mfa完成登录

简化版MFA绑定组件示例:

import { useState, useEffect } from 'react';
import QRCode from 'qrcode.react';
import axios from 'axios';

const MfaSetupPage = () => {
  const [qrContent, setQrContent] = useState('');
  const [manualKey, setManualKey] = useState('');
  const [verifyCode, setVerifyCode] = useState('');
  const [recoveryCodes, setRecoveryCodes] = useState([]);

  useEffect(() => {
    const fetchSetupInfo = async () => {
      const res = await axios.get('/api/mfa/get-setup-info');
      setQrContent(res.data.qrCodeContent);
      setManualKey(res.data.manualKey);
    };
    fetchSetupInfo();
  }, []);

  const handleEnableMfa = async () => {
    const res = await axios.post('/api/mfa/enable', { code: verifyCode });
    setRecoveryCodes(res.data.recoveryCodes);
  };

  return (
    <div className="mfa-setup">
      <h2>绑定微软验证器</h2>
      <QRCode value={qrContent} size={220} level="M" />
      <p>无法扫码?手动输入密钥:<strong>{manualKey}</strong></p>
      <input
        type="text"
        maxLength={6}
        value={verifyCode}
        onChange={(e) => setVerifyCode(e.target.value)}
        placeholder="请输入验证器显示的6位数字"
      />
      <button onClick={handleEnableMfa}>启用多因素认证</button>
      {recoveryCodes.length > 0 && (
        <div className="recovery-codes">
          <h3>启用成功,请妥善保存以下恢复码,丢失验证器时可用于登录</h3>
          {recoveryCodes.map(code => <p key={code}>{code}</p>)}
        </div>
      )}
    </div>
  );
};

export default MfaSetupPage;

注意事项:

  1. 生产环境需要对MFA相关接口做频率限制,防止暴力破解6位验证码
  2. 恢复码仅在生成时返回明文,后端存储的是哈希值,无法二次查看,需要提示用户妥善保存
  3. 要保证服务器时间准确,TOTP校验依赖时间同步,误差超过30秒会导致验证码验证失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 15:54:05