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

Blazor WASM如何调起设备原生Touch ID/Face ID生物识别弹窗

可行性结论

完全可以实现。你需要的「仅调起系统原生生物识别弹窗、后续认证逻辑自主处理、无第三方服务依赖」的需求,完全可以通过浏览器原生标准能力实现,不需要接入任何重型第三方认证库。
目前网上多数生物认证相关库,本质是对W3C标准Web Authentication API(WebAuthn)做了封装,再绑定了自家的服务端校验逻辑。如果不需要第三方提供的认证服务,完全可以直接在Blazor WASM中调用原生WebAuthn API,覆盖所有你提到的运行环境:

  • iOS 14+ 全量浏览器(Safari、Chrome、Edge等,iOS 15+完整支持Face ID、Touch ID调起)
  • Android 7+ 支持生物识别的主流浏览器(Chrome、Edge、火狐等)
  • 桌面端Chrome 67+、Edge 79+、Safari 13+、Firefox 60+(支持Windows Hello、macOS Touch ID系统验证弹窗)
    所有环境下调起的都是系统级原生验证弹窗,没有中间自定义弹窗层,体验和原生App生物识别完全一致。
具体实现路径

整个实现不需要引入任何第三方NuGet包或JS依赖,总代码量不到200行,完全自主可控。

  • 第一步:基础环境检测
    调用生物识别前先判断当前设备、浏览器是否支持平台级生物认证,避免不兼容环境报错,核心判断逻辑是检测浏览器是否暴露PublicKeyCredential接口,以及是否配置了可用的系统生物识别器。
  • 第二步:封装轻量JS互操作层
    Blazor WASM无法直接调用浏览器原生API,只需要写一个极简的JS模块做桥接即可,示例代码如下:
    首先在wwwroot/js目录下新建webAuthn.js文件:
    export async function checkBiometricSupport() {
        if (!window.PublicKeyCredential) {
            return { supported: false, reason: "当前浏览器不支持生物认证能力" };
        }
        const platformAuthAvailable = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
        return {
            supported: platformAuthAvailable,
            reason: platformAuthAvailable ? "" : "当前设备未配置系统生物识别(Touch ID/Face ID/Windows Hello等)"
        };
    }
    
    export async function triggerBiometricPrompt(challenge, credentialId = null) {
        const publicKeyConfig = {
            challenge: Uint8Array.from(challenge, c => c.charCodeAt(0)),
            rpId: window.location.hostname,
            userVerification: "required", // 强制要求生物验证通过,不允许跳过
            timeout: 60000,
            allowCredentials: credentialId ? [{
                id: Uint8Array.from(atob(credentialId), c => c.charCodeAt(0)),
                type: "public-key",
                transports: ["internal"]
            }] : []
        };
    
        try {
            // 该行代码执行后会直接调起系统原生生物识别弹窗
            const authResult = await navigator.credentials.get({ publicKey: publicKeyConfig });
            return {
                success: true,
                authData: Array.from(new Uint8Array(authResult.response.authenticatorData)),
                clientData: Array.from(new Uint8Array(authResult.response.clientDataJSON)),
                signature: Array.from(new Uint8Array(authResult.response.signature))
            };
        } catch (err) {
            return {
                success: false,
                error: err.message || "生物识别验证失败"
            };
        }
    }
    
    // 仅做本地验证时,用来生成本地存储的测试凭证
    export async function createLocalTestCredential() {
        const createConfig = {
            publicKey: {
                rp: { id: window.location.hostname, name: window.location.hostname },
                user: { id: new Uint8Array(16), name: "local_user", displayName: "Local User" },
                challenge: new Uint8Array(32),
                pubKeyCredParams: [{ type: "public-key", alg: -7 }],
                authenticatorSelection: { authenticatorAttachment: "platform", userVerification: "required" },
                timeout: 60000
            }
        };
        const cred = await navigator.credentials.create(createConfig);
        return btoa(String.fromCharCode(...new Uint8Array(cred.rawId)));
    }
    
    然后在Blazor组件/Razor页面中调用即可,示例代码:
    @inject IJSRuntime JS
    @inject IYourOwnAuthService ExistingAuthService // 替换成你自己现有的认证服务
    
    <button @onclick="DoBiometricAuth" disabled="@!_isSupportBiometric" class="btn btn-primary">
        生物识别验证
    </button>
    
    @code {
        private IJSObjectReference? _webAuthnModule;
        private bool _isSupportBiometric;
        private string? _localCredentialId;
        private const string LocalCredStorageKey = "local_biometric_cred_id";
    
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                _webAuthnModule = await JS.InvokeAsync<IJSObjectReference>("import", "./js/webAuthn.js");
                var supportRes = await _webAuthnModule.InvokeAsync<SupportCheckResult>("checkBiometricSupport");
                _isSupportBiometric = supportRes.Supported;
    
                // 仅做本地验证时,检查是否已有本地测试凭证,没有则创建
                if (_isSupportBiometric)
                {
                    _localCredentialId = await JS.InvokeAsync<string?>("localStorage.getItem", LocalCredStorageKey);
                    if (string.IsNullOrEmpty(_localCredentialId))
                    {
                        _localCredentialId = await _webAuthnModule.InvokeAsync<string>("createLocalTestCredential");
                        await JS.InvokeVoidAsync("localStorage.setItem", LocalCredStorageKey, _localCredentialId);
                    }
                }
                StateHasChanged();
            }
        }
    
        private async Task DoBiometricAuth()
        {
            if (!_isSupportBiometric || _webAuthnModule == null) return;
            
            // 从你自己现有认证服务生成随机挑战码,用于防重放
            var challenge = await ExistingAuthService.GenerateAuthChallenge();
            var authRes = await _webAuthnModule.InvokeAsync<BiometricAuthResult>(
                "triggerBiometricPrompt", 
                challenge, 
                _localCredentialId
            );
    
            if (authRes.Success)
            {
                // 生物识别通过,直接走你自己现有的认证逻辑即可,不需要依赖任何第三方服务
                await ExistingAuthService.CompleteAuthProcess(authRes);
            }
            else
            {
                // 自行处理识别失败场景:用户取消、识别不匹配、超时等
                Console.WriteLine($"验证失败:{authRes.Error}");
            }
        }
    
        // 接口返回结构定义
        public class SupportCheckResult
        {
            public bool Supported { get; set; }
            public string Reason { get; set; } = string.Empty;
        }
    
        public class BiometricAuthResult
        {
            public bool Success { get; set; }
            public byte[] AuthData { get; set; } = [];
            public byte[] ClientData { get; set; } = [];
            public byte[] Signature { get; set; } = [];
            public string Error { get; set; } = string.Empty;
        }
    }
    
  • 第三步:对接自有认证逻辑
    拿到生物识别通过的结果后,完全可以根据自己的需求对接现有认证体系,没有任何限制:
    • 如果只需要确认当前操作是设备本人发起,直接在收到Success=true的结果后放行原有业务逻辑即可,不需要额外校验
    • 如果需要做高强度的防篡改校验,可以把返回的签名、认证数据、挑战码回传给你自己的服务端,用你自己存储的公钥做签名校验,全程不需要调用第三方接口
    • 可以直接复用现有登录、JWT签发、权限校验等逻辑,不需要重构原有认证体系
注意事项
  • WebAuthn标准要求生产环境必须使用HTTPS协议,本地调试localhost环境不受此限制
  • 上述代码中创建的本地测试凭证仅存储在用户设备本地,不会上传到任何服务器,完全符合隐私要求
  • 如果后续需要扩展免密登录、设备绑定等能力,只需要在现有代码基础上补充WebAuthn凭证注册、服务端公钥存储逻辑即可,不需要重构现有代码
  • 不需要申请任何第三方服务权限、密钥,没有额外成本

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 09:12:22