Share via


在 ASP.NET Core Blazor Web 應用程式中為 TOTP 驗證器應用程式啟用產生 QR 代碼

本文說明如何設定 ASP.NET Core Blazor Web 應用程式搭配 TOTP 驗證器應用程式產生 QR 代碼。

若需雙因素驗證 (2FA) 搭配使用以時間為基礎的單次密碼演算法 (TOTP) 驗證器應用程式的簡介,請參閱在 ASP.NET Core Web 應用程式中為 TOTP 驗證器應用程式啟用產生 QR 代碼。

將 Enable Authenticator 元件 Scaffold 到應用程式中

請遵循 ASP.NET Core 專案中 Scaffold Identity 中的指引,Pages\Manage\EnableAuthenticator Scaffold 到應用程式中。

注意

雖然在此範例中只有選取 EnableAuthenticator 元件以進行 Scaffolding,但 Scaffolding 目前會將所有 Identity 元件新增至應用程式。 此外,在 Scaffolding 到應用程式中的流程期間,可能會擲回例外狀況。 如果資料庫移轉時發生例外狀況,請停止應用程式,並在每次發生例外狀況時重新啟動應用程式。 如需詳細資訊,請參閱 Blazor Web 應用程式的 Scaffolding 例外狀況 (dotnet/Scaffolding #2694)

在執行移轉時請耐心等候。 視系統的速度而定,資料庫移轉最多可能需要一或兩分鐘的時間才能完成。

如需詳細資訊,請參閱 ASP.NET Core 專案中的 Scaffold Identity。 若需使用 .NET CLI 而非 Visual Studio 的指導,請參閱dotnet aspnet-codegenerator 命令

將 QR 代碼新增至 2FA 設定頁面

這些指示會使用 Shim Sangminqrcode.js:適用於 JavaScript 的跨瀏覽器 QRCode 產生器 (davidshimjs/qrcodejs GitHub 存放庫)。

qrcode.min.js 程式庫下載至解決方案伺服器專案的 wwwroot 資料夾。 該程式庫無相依性。

App 元件 (Components/App.razor) 中,將程式庫指令碼參考放在 Blazor 的 <script> 標籤之後:

<script src="qrcode.min.js"></script>

EnableAuthenticator 元件是應用程式中 QR 代碼系統的一部分,並且會向使用者顯示 QR 代碼、採用具有增強式導覽的靜態伺服器端轉譯 (靜態 SSR)。 因此,元件在載入或在增強式導覽下更新時,就無法執行一般指令碼。 載入頁面時,您需要採取額外的步驟才能觸發在 UI 中載入 QR 代碼。 若要完成載入 QR 代碼,請採用如 ASP.NET CoreBlazor JavaScript 搭配靜態伺服器端轉譯 (靜態 SSR) 中所述的方法。

將下列 JavaScript 初始設定式 新增至伺服器專案的 wwwroot 資料夾。 {NAME} 預留位置必須是應用程式組件的名稱,Blazor 才能自動尋找和載入檔案。 若伺服器應用程式組件的名稱是 BlazorSample,則該檔案會命名為 BlazorSample.lib.module.js

wwwroot/{NAME}.lib.module.js

const pageScriptInfoBySrc = new Map();

function registerPageScriptElement(src) {
  if (!src) {
    throw new Error('Must provide a non-empty value for the "src" attribute.');
  }

  let pageScriptInfo = pageScriptInfoBySrc.get(src);

  if (pageScriptInfo) {
    pageScriptInfo.referenceCount++;
  } else {
    pageScriptInfo = { referenceCount: 1, module: null };
    pageScriptInfoBySrc.set(src, pageScriptInfo);
    initializePageScriptModule(src, pageScriptInfo);
  }
}

function unregisterPageScriptElement(src) {
  if (!src) {
    return;
  }

  const pageScriptInfo = pageScriptInfoBySrc.get(src);
  
  if (!pageScriptInfo) {
    return;
  }

  pageScriptInfo.referenceCount--;
}

async function initializePageScriptModule(src, pageScriptInfo) {
  if (src.startsWith("./")) {
    src = new URL(src.substr(2), document.baseURI).toString();
  }

  const module = await import(src);

  if (pageScriptInfo.referenceCount <= 0) {
    return;
  }

  pageScriptInfo.module = module;
  module.onLoad?.();
  module.onUpdate?.();
}

function onEnhancedLoad() {
  for (const [src, { module, referenceCount }] of pageScriptInfoBySrc) {
    if (referenceCount <= 0) {
      module?.onDispose?.();
      pageScriptInfoBySrc.delete(src);
    }
  }

  for (const { module } of pageScriptInfoBySrc.values()) {
    module?.onUpdate?.();
  }
}

export function afterWebStarted(blazor) {
  customElements.define('page-script', class extends HTMLElement {
    static observedAttributes = ['src'];

    attributeChangedCallback(name, oldValue, newValue) {
      if (name !== 'src') {
        return;
      }

      this.src = newValue;
      unregisterPageScriptElement(oldValue);
      registerPageScriptElement(newValue);
    }

    disconnectedCallback() {
      unregisterPageScriptElement(this.src);
    }
  });

  blazor.addEventListener('enhancedload', onEnhancedLoad);
}

將下列共用 PageScript 元件新增至伺服器應用程式。

Components/PageScript.razor

<page-script src="@Src"></page-script>

@code {
    [Parameter]
    [EditorRequired]
    public string Src { get; set; } = default!;
}

為位於 Components/Account/Pages/Manage/EnableAuthenticator.razorEnableAuthenticator 元件,新增下列共置的 JS 檔案onLoad 函式會使用元件 @code 區塊中 GenerateQrCodeUri 方法所產生的 QR 代碼 URI,搭配 Sangmin 的 qrcode.js 程式庫來建立 QR 代碼。

Components/Account/Pages/Manage/EnableAuthenticator.razor.js

export function onLoad() {
  const uri = document.getElementById('qrCodeData').getAttribute('data-url');
  new QRCode(document.getElementById('qrCode'), uri);
}

EnableAuthenticator 元件中的 <PageTitle> 元件底下,新增 PageScript 元件,並提供共置 JS 檔案的路徑:

<PageScript Src="./Components/Account/Pages/Manage/EnableAuthenticator.razor.js" />

注意

使用 PageScript 元件的替代方法是使用在afterWebStartedJS 初始設定式中註冊的事件接聽程式 (blazor.addEventListener("enhancedload", {CALLBACK})),來接聽增強式導覽所導致的頁面更新。 回呼 ({CALLBACK} 預留位置) 會執行 QR 代碼初始化邏輯。

使用回呼方法搭配 enhancedload,即使未轉譯 QR 代碼 <div>,該程式碼也會針對每個增強式導覽執行。 因此,您必須先新增額外的程式碼,才能在執行新增 QR 代碼的程式碼之前檢查是否有 <div>

刪除包含 QR 代碼指示的 <div> 元素:

- <div class="alert alert-info">
-     Learn how to <a href="https://go.microsoft.com/fwlink/?Linkid=852423">enable 
-     QR code generation</a>.
- </div>

找出應顯示 QR 代碼的兩個 <div> 元素,以及 QR 代碼資料儲存在頁面中的位置。

進行下列變更:

  • 針對空的 <div>,請為元素提供 qrCodeid
  • 針對具有 data-url 屬性的 <div>,請為元素提供 qrCodeDataid
- <div></div>
- <div data-url="@authenticatorUri"></div>
+ <div id="qrCode"></div>
+ <div id="qrCodeData" data-url="@authenticatorUri"></div>

變更 EnableAuthenticator 元件 GenerateQrCodeUri 方法中的網站名字。 預設值是 Microsoft.AspNetCore.Identity.UI。 將值變更為有意義的網站名稱讓使用者可以輕鬆識別,不會與驗證器應用程式中其他應用程式的 QR 代碼混淆。 請保留值 URL 的編碼。 開發人員通常會設定符合公司名稱的網站名稱。 範例:Yahoo、Amazon、Etsy、Microsoft、Zoho。

在下列範例中,{SITE NAME} 預留位置是網站 (公司) 名稱的位置:

private string GenerateQrCodeUri(string email, string unformattedKey)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        AuthenticatorUriFormat,
-       UrlEncoder.Encode("Microsoft.AspNetCore.Identity.UI"),
+       UrlEncoder.Encode("{SITE NAME}"),
        UrlEncoder.Encode(email),
        unformattedKey);
}

執行應用程式並確定 QR 代碼可掃描和有效。

參考來源中的 EnableAuthenticator 元件

您可在參考來源中查看 EnableAuthenticator 元件:

參考來源中的 EnableAuthenticator 元件

注意

.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤

其他資源