使用 Azure Blob 儲存體和 JavaScript 建立使用者委派 SAS 權杖

本文說明如何在 JavaScript 的 Azure Blob 儲存體用戶端程式庫 v12 中,建立使用者委派 SAS 權杖。 2018-11-09 版引進的使用者委派 SAS 會經過 Microsoft Entra 認證保護,且僅支援 Blob 服務:

  • 授與現有容器存取權。
  • 授與建立、使用和刪除 Blob 的存取權

若要建立使用者委派 SAS,用戶端必須具有呼叫 blobServiceClient.getUserDelegationKey 作業的許可權。 此作業傳回的金鑰可用來簽署使用者委派 SAS。 呼叫此作業的安全性主體必須被指派包含 Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey/action 的 RBAC 角色。

授與具有 SAS 用戶端的權限包含了授與安全性主體 (要求使用者委派金鑰) 的權限,以及在簽署的權限 (sp) 欄位中,授與 SAS 權杖資源的權限。 如果透過 RBAC 授與安全性主體的權限並未在 SAS 權杖上授與,則不會將該權限授與嘗試使用 SAS 存取資源的用戶端。

範例程式碼片段可在 GitHub 中以可執行的 Node.js 檔案形式來取得。

套件 (npm) | 範例 | API 參考 | 程式庫原始程式碼 | 提供意見反應

使用者委派 SAS 權杖的最佳做法

由於具有 SAS 權杖的任何人都可以用其來存取容器和 Blob,因此您應該使用最嚴格的權限來定義 SAS 權杖 (該權限仍允許權杖完成所需的工作)。

SAS 權杖的最佳做法

在 Azure Cloud 中使用 DefaultAzureCredential

若要在不使用密碼的情況下向 Azure 驗證,請設定受控識別。 這可讓您的程式碼使用 DefaultAzureCredential

若要設定 Azure 雲端的受控識別:

  • 建立受控識別
  • 為身分識別設定適當的儲存體角色
  • 設定您的 Azure 環境以搭配受控識別

當這兩個工作完成時,請使用 DefaultAzureCredential,而不是連接字串或帳戶金鑰。 這可讓您的所有環境都使用相同的原始程式碼 ,而不需要在原始程式碼中使用密碼。

在本機開發過程中使用 DefaultAzureCredential

在本機開發環境中,您的 Azure 身分識別 (您用來登入 Azure 入口網站的個人或開發帳戶) 需要向 Azure 驗證,才能在本機和雲端執行階段中使用相同程式碼。

容器:將必要的相依性新增至您的應用程式

包含建立容器 SAS 權杖所需的相依性。

const {
    DefaultAzureCredential
} = require('@azure/identity');
const {
    ContainerClient,
    BlobServiceClient,
    ContainerSASPermissions,
    generateBlobSASQueryParameters,
    SASProtocol
} = require('@azure/storage-blob');

// used for local environment variables
require('dotenv').config();

容器:取得環境變數

Blob 儲存體帳戶名稱和容器名稱是建立容器 SAS 權杖所需的最小值:

// Get environment variables for DefaultAzureCredential
const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

使用 DefaultAzureCredential 建立 SAS

使用 DefaultAzureCredential 建立 SAS 權杖需要下列概念步驟:

  • 設定 DefaultAzureCredential
    • 本機開發 - 使用個人身分識別並設定儲存體的角色
    • Azure 雲端 - 建立受控識別
  • 使用 DefaultAzureCredential 透過 UserDelegationKey 取得使用者委派金鑰
  • 使用使用者委派金鑰搭配 generateBlobSASQueryParameters 的適當欄位來建構 SAS 權杖

容器:使用 DefaultAzureCredential 建立 SAS 權杖

設定身分識別後,請使用下列程式碼為現有帳戶和容器建立使用者委派 SAS 權杖

// Server creates User Delegation SAS Token for container
async function createContainerSas() {

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

    // Best practice: create time limits
    const TEN_MINUTES = 10 * 60 * 1000;
    const NOW = new Date();

    // Best practice: set start time a little before current time to 
    // make sure any clock issues are avoided
    const TEN_MINUTES_BEFORE_NOW = new Date(NOW.valueOf() - TEN_MINUTES);
    const TEN_MINUTES_AFTER_NOW = new Date(NOW.valueOf() + TEN_MINUTES);

    // Best practice: use managed identity - DefaultAzureCredential
    const blobServiceClient = new BlobServiceClient(
        `https://${accountName}.blob.core.windows.net`,
        new DefaultAzureCredential()
      );

    // Best practice: delegation key is time-limited  
    // When using a user delegation key, container must already exist 
    const userDelegationKey = await blobServiceClient.getUserDelegationKey(
        TEN_MINUTES_BEFORE_NOW, 
        TEN_MINUTES_AFTER_NOW
    );

    // Need only list permission to list blobs 
    const containerPermissionsForAnonymousUser = "l";

    // Best practice: SAS options are time-limited
    const sasOptions = {
        containerName,                                           
        permissions: ContainerSASPermissions.parse(containerPermissionsForAnonymousUser), 
        protocol: SASProtocol.HttpsAndHttp,
        startsOn: TEN_MINUTES_BEFORE_NOW,
        expiresOn: TEN_MINUTES_AFTER_NOW
    };
 
    const sasToken = generateBlobSASQueryParameters(
        sasOptions,
        userDelegationKey,
        accountName 
    ).toString();

    return sasToken;
}

上述伺服器程式碼會建立值的流程,以建立容器 SAS 權杖:

建立容器 SAS 權杖之後,您可以將其提供給將取用權杖的用戶端。 用戶端接著可以用來列出容器中的 Blob。 用戶端程式代碼範例會顯示如何將 SAS 測試為取用者。

容器:使用 SAS 權杖

建立容器 SAS 權杖之後,請使用權杖。 作為使用 SAS 權杖的範例,您可以:

  • 建構完整的 URL,包括容器名稱和查詢字串。 查詢字串是 SAS 權杖。
  • 使用容器 URL 建立 ContainerClient
  • 使用用戶端:在此範例中,使用 listBlobsFlat 列出容器中的 Blob。
// Client or another process uses SAS token to use container
async function listBlobs(sasToken){

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;
    
    // Create Url
    // SAS token is the query string with typical `?` delimiter
    const sasUrl = `https://${accountName}.blob.core.windows.net/${containerName}?${sasToken}`;
    console.log(`\nContainerUrl = ${sasUrl}\n`);

    // Create container client from SAS token url
    const containerClient = new ContainerClient(sasUrl);

    let i = 1;

    // List blobs in container
    for await (const blob of containerClient.listBlobsFlat()) {
        console.log(`Blob ${i++}: ${blob.name}`);
    }    
}

容器:將必要的相依性新增至您的應用程式

包含建立 Blob SAS 權杖所需的相依性。

const {
    DefaultAzureCredential
} = require('@azure/identity');
const {
    BlockBlobClient,
    BlobServiceClient,
    BlobSASPermissions,
    generateBlobSASQueryParameters,
    SASProtocol
} = require('@azure/storage-blob');

// used for local environment variables
require('dotenv').config();

Blob:設定環境變數

Blob 儲存體帳戶名稱和容器名稱是建立 Blob SAS 權杖所需的最小值:

const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

當您需要建立 Blob SAS 權杖時,必須有 Blob 名稱才能建立 SAS 權杖。 這會預先決定,例如隨機 Blob 名稱、使用者提交的 Blob 名稱,或從您的應用程式產生的名稱。

// Create random blob name for text file
const blobName = `${(0|Math.random()*9e6).toString(36)}.txt`;

容器:使用 DefaultAzureCredential 建立 SAS 權杖

設定身分識別後,請使用下列程式碼為現有帳戶和容器建立使用者委派 SAS 權杖

// Server creates User Delegation SAS Token for blob
async function createBlobSas(blobName) {

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

    // Best practice: create time limits
    const TEN_MINUTES = 10 * 60 * 1000;
    const NOW = new Date();

    // Best practice: set start time a little before current time to 
    // make sure any clock issues are avoided
    const TEN_MINUTES_BEFORE_NOW = new Date(NOW.valueOf() - TEN_MINUTES);
    const TEN_MINUTES_AFTER_NOW = new Date(NOW.valueOf() + TEN_MINUTES);

    // Best practice: use managed identity - DefaultAzureCredential
    const blobServiceClient = new BlobServiceClient(
        `https://${accountName}.blob.core.windows.net`,
        new DefaultAzureCredential()
      );

    // Best practice: delegation key is time-limited  
    // When using a user delegation key, container must already exist 
    const userDelegationKey = await blobServiceClient.getUserDelegationKey(
        TEN_MINUTES_BEFORE_NOW, 
        TEN_MINUTES_AFTER_NOW
    );

    // Need only create/write permission to upload file
    const blobPermissionsForAnonymousUser = "cw"

    // Best practice: SAS options are time-limited
    const sasOptions = {
        blobName,
        containerName,                                           
        permissions: BlobSASPermissions.parse(blobPermissionsForAnonymousUser), 
        protocol: SASProtocol.HttpsAndHttp,
        startsOn: TEN_MINUTES_BEFORE_NOW,
        expiresOn: TEN_MINUTES_AFTER_NOW
    };
 
    const sasToken = generateBlobSASQueryParameters(
        sasOptions,
        userDelegationKey,
        accountName 
    ).toString();

    return sasToken;
}

上述程式碼會建立值的流程,以建立容器 SAS 權杖:

建立容器 SAS 權杖之後,您可以將其提供給將取用權杖的用戶端。 用戶端接著可以將其用來上傳 Blob。 用戶端程式代碼範例會顯示如何將 SAS 測試為取用者。

Blob:使用 SAS 權杖

建立 Blob SAS 權杖之後,請使用權杖。 作為使用 SAS 權杖的範例,您可以:

  • 建構完整的 URL,包括容器名稱、Blob 名稱和查詢字串。 查詢字串是 SAS 權杖。
  • 使用容器 URL 建立 BlockBlobClient
  • 使用用戶端:在此範例中,透過上傳來上傳 Bob。
// Client or another process uses SAS token to upload content to blob
async function uploadStringToBlob(blobName, sasToken, textAsString){

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

    // Create Url SAS token as query string with typical `?` delimiter
    const sasUrl = `https://${accountName}.blob.core.windows.net/${containerName}/${blobName}?${sasToken}`;
    console.log(`\nBlobUrl = ${sasUrl}\n`);

    // Create blob client from SAS token url
    const blockBlobClient = new BlockBlobClient(sasUrl);

    // Upload string
    await blockBlobClient.upload(textAsString, textAsString.length, undefined);    
}

另請參閱