Proteger uma conta do Azure Maps com um token SAS

Este artigo descreve como criar uma conta do Azure Maps com um token SAS armazenado com segurança que você pode usar para chamar a API REST do Azure Maps.

Pré-requisitos

  • Uma subscrição do Azure. Se ainda não tiver uma conta do Azure, inscreva-se para obter uma conta gratuita.

  • Permissão de função de proprietário na assinatura do Azure. Você precisa das permissões de Proprietário para:

    • Crie um cofre de chaves no Cofre de Chaves do Azure.
    • Crie uma identidade gerida atribuída pelo utilizador.
    • Atribua uma função à identidade gerenciada.
    • Crie uma conta do Azure Maps.
  • CLI do Azure instalada para implantar os recursos.

Cenário de exemplo: armazenamento seguro de token SAS

Uma credencial de token SAS concede o nível de acesso especificado a qualquer pessoa que a detenha, até que o token expire ou o acesso seja revogado. Os aplicativos que usam autenticação de token SAS devem armazenar as chaves com segurança.

Esse cenário armazena com segurança um token SAS como um segredo no Cofre de Chaves e distribui o token em um cliente público. Os eventos do ciclo de vida do aplicativo podem gerar novos tokens SAS sem interromper conexões ativas que usam tokens existentes.

Para obter mais informações sobre como configurar o Cofre da Chave, consulte o guia do desenvolvedor do Cofre da Chave do Azure.

O cenário de exemplo a seguir usa duas implantações de modelo do Azure Resource Manager (ARM) para executar as seguintes etapas:

  1. Criar um cofre de chaves.
  2. Crie uma identidade gerida atribuída pelo utilizador.
  3. Atribua a função de Leitor de Dados do Azure Maps (RBAC) do Azure Maps à identidade gerenciada atribuída pelo usuário.
  4. Crie uma conta do Azure Maps com uma configuração de Compartilhamento de Recursos entre Origens (CORS) e anexe a identidade gerenciada atribuída pelo usuário.
  5. Crie e salve um token SAS no cofre de chaves do Azure.
  6. Recupere o segredo do token SAS do cofre de chaves.
  7. Crie uma solicitação de API REST do Azure Maps que use o token SAS.

Quando terminar, você verá os resultados da API REST do Azure Maps Search Address (Non-Batch) no PowerShell com a CLI do Azure. Os recursos do Azure são implantados com permissões para se conectar à conta do Azure Maps. Há controles para limite máximo de taxa, regiões permitidas, localhost política CORS configurada e RBAC do Azure.

Implantação de recursos do Azure com a CLI do Azure

As etapas a seguir descrevem como criar e configurar uma conta do Azure Maps com autenticação de token SAS. Neste exemplo, a CLI do Azure é executada em uma instância do PowerShell.

  1. Entre na sua assinatura do Azure com az logino .

  2. Registe o Cofre da Chave, as Identidades Geridas e o Azure Maps para a sua subscrição.

    az provider register --namespace Microsoft.KeyVault
    az provider register --namespace Microsoft.ManagedIdentity
    az provider register --namespace Microsoft.Maps
    
  3. Recupere sua ID de objeto do Microsoft Entra.

    $id = $(az rest --method GET --url 'https://graph.microsoft.com/v1.0/me?$select=id' --headers 'Content-Type=application/json' --query "id")
    
  4. Crie um arquivo de modelo chamado prereq.azuredeploy.json com o seguinte conteúdo:

    {
        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
            "location": {
                "type": "string",
                "defaultValue": "[resourceGroup().location]",
                "metadata": {
                    "description": "Specifies the location for all the resources."
                }
            },
            "keyVaultName": {
                "type": "string",
                "defaultValue": "[concat('vault', uniqueString(resourceGroup().id))]",
                "metadata": {
                    "description": "Specifies the name of the key vault."
                }
            },
            "userAssignedIdentityName": {
                "type": "string",
                "defaultValue": "[concat('identity', uniqueString(resourceGroup().id))]",
                "metadata": {
                    "description": "The name for your managed identity resource."
                }
            },
            "objectId": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the object ID of a user, service principal, or security group in the Azure AD tenant for the vault. The object ID must be unique for the set of access policies. Get it by using Get-AzADUser or Get-AzADServicePrincipal cmdlets."
                }
            },
            "secretsPermissions": {
                "type": "array",
                "defaultValue": [
                    "list",
                    "get",
                    "set"
                ],
                "metadata": {
                    "description": "Specifies the permissions to secrets in the vault. Valid values are: all, get, list, set, delete, backup, restore, recover, and purge."
                }
            }
        },
        "resources": [
            {
                "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
                "name": "[parameters('userAssignedIdentityName')]",
                "apiVersion": "2018-11-30",
                "location": "[parameters('location')]"
            },
            {
                "apiVersion": "2021-04-01-preview",
                "type": "Microsoft.KeyVault/vaults",
                "name": "[parameters('keyVaultName')]",
                "location": "[parameters('location')]",
                "properties": {
                    "tenantId": "[subscription().tenantId]",
                    "sku": {
                        "name": "Standard",
                        "family": "A"
                    },
                    "enabledForTemplateDeployment": true,
                    "accessPolicies": [
                        {
                            "objectId": "[parameters('objectId')]",
                            "tenantId": "[subscription().tenantId]",
                            "permissions": {
                                "secrets": "[parameters('secretsPermissions')]"
                            }
                        }
                    ]
                }
            }
        ],
        "outputs": {
            "userIdentityResourceId": {
                "type": "string",
                "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]"
            },
            "userAssignedIdentityPrincipalId": {
                "type": "string",
                "value": "[reference(parameters('userAssignedIdentityName')).principalId]"
            },
            "keyVaultName": {
                "type": "string",
                "value": "[parameters('keyVaultName')]"
            }
        }
    }
    
    
  5. Implante os recursos de pré-requisito criados na etapa anterior. Forneça o seu próprio valor para <group-name>. Certifique-se de usar o mesmo location que a conta do Azure Maps.

    az group create --name <group-name> --location "East US"
    $outputs = $(az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./prereq.azuredeploy.json" --parameters objectId=$id --query "[properties.outputs.keyVaultName.value, properties.outputs.userAssignedIdentityPrincipalId.value, properties.outputs.userIdentityResourceId.value]" --output tsv)
    
  6. Crie um arquivo de modelo azuredeploy.json para provisionar a conta do Azure Maps, a atribuição de função e o token SAS.

    Nota

    Aposentadoria da camada de preços do Azure Maps Gen1

    O nível de preços Gen1 foi preterido e será desativado em 15/09/26. O nível de preços Gen2 substitui o nível de preços Gen1 (S0 e S1). Se sua conta do Azure Maps tiver o nível de preço Gen1 selecionado, você poderá alternar para o preço Gen2 antes de ser desativado, caso contrário, ele será atualizado automaticamente. Para obter mais informações, consulte Gerenciar a camada de preços da sua conta do Azure Maps.

    {
        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
            "location": {
                "type": "string",
                "defaultValue": "[resourceGroup().location]",
                "metadata": {
                    "description": "Specifies the location for all the resources."
                }
            },
            "keyVaultName": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the resourceId of the key vault."
                }
            },
            "accountName": {
                "type": "string",
                "defaultValue": "[concat('map', uniqueString(resourceGroup().id))]",
                "metadata": {
                    "description": "The name for your Azure Maps account."
                }
            },
            "userAssignedIdentityResourceId": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the resourceId for the user assigned managed identity resource."
                }
            },
            "userAssignedIdentityPrincipalId": {
                "type": "string",
                "metadata": {
                    "description": "Specifies the resourceId for the user assigned managed identity resource."
                }
            },
            "pricingTier": {
                "type": "string",
                "allowedValues": [
                    "S0",
                    "S1",
                    "G2"
                ],
                "defaultValue": "G2",
                "metadata": {
                    "description": "The pricing tier for the account. Use S0 for small-scale development. Use S1 or G2 for large-scale applications."
                }
            },
            "kind": {
                "type": "string",
                "allowedValues": [
                    "Gen1",
                    "Gen2"
                ],
                "defaultValue": "Gen2",
                "metadata": {
                    "description": "The pricing tier for the account. Use Gen1 for small-scale development. Use Gen2 for large-scale applications."
                }
            },
            "guid": {
                "type": "string",
                "defaultValue": "[guid(resourceGroup().id)]",
                "metadata": {
                    "description": "Input string for new GUID associated with assigning built in role types."
                }
            },
            "startDateTime": {
                "type": "string",
                "defaultValue": "[utcNow('u')]",
                "metadata": {
                    "description": "Current Universal DateTime in ISO 8601 'u' format to use as the start of the SAS token."
                }
            },
            "duration" : {
                "type": "string",
                "defaultValue": "P1Y",
                "metadata": {
                    "description": "The duration of the SAS token. P1Y is maximum, ISO 8601 format is expected."
                }
            },
            "maxRatePerSecond": {
                "type": "int",
                "defaultValue": 500,
                "minValue": 1,
                "maxValue": 500,
                "metadata": {
                    "description": "The approximate maximum rate per second the SAS token can be used."
                }
            },
            "signingKey": {
                "type": "string",
                "defaultValue": "primaryKey",
                "allowedValues": [
                    "primaryKey",
                    "seconaryKey"
                ],
                "metadata": {
                    "description": "The specified signing key which will be used to create the SAS token."
                }
            },
            "allowedOrigins": {
                "type": "array",
                "defaultValue": [],
                "maxLength": 10,
                "metadata": {
                    "description": "The specified application's web host header origins (example: https://www.azure.com) which the Azure Maps account allows for CORS."
                }
            }, 
            "allowedRegions": {
                "type": "array",
                "defaultValue": [],
                "metadata": {
                    "description": "The specified SAS token allowed locations where the token may be used."
                }
            }
        },
        "variables": {
            "accountId": "[resourceId('Microsoft.Maps/accounts', parameters('accountName'))]",
            "Azure Maps Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '423170ca-a8f6-4b0f-8487-9e4eb8f49bfa')]",
            "sasParameters": {
                "signingKey": "[parameters('signingKey')]",
                "principalId": "[parameters('userAssignedIdentityPrincipalId')]",
                "maxRatePerSecond": "[parameters('maxRatePerSecond')]",
                "start": "[parameters('startDateTime')]",
                "expiry": "[dateTimeAdd(parameters('startDateTime'), parameters('duration'))]",
                "regions": "[parameters('allowedRegions')]"
            }
        },
        "resources": [
            {
                "name": "[parameters('accountName')]",
                "type": "Microsoft.Maps/accounts",
                "apiVersion": "2023-06-01",
                "location": "[parameters('location')]",
                "sku": {
                    "name": "[parameters('pricingTier')]"
                },
                "kind": "[parameters('kind')]",
                "properties": {
                    "cors": {
                        "corsRules": [
                            {
                                "allowedOrigins": "[parameters('allowedOrigins')]"
                            }
                        ]
                    }
                },
                "identity": {
                    "type": "UserAssigned",
                    "userAssignedIdentities": {
                        "[parameters('userAssignedIdentityResourceId')]": {}
                    }
                }
            },
            {
                "apiVersion": "2020-04-01-preview",
                "name": "[concat(parameters('accountName'), '/Microsoft.Authorization/', parameters('guid'))]",
                "type": "Microsoft.Maps/accounts/providers/roleAssignments",
                "dependsOn": [
                    "[parameters('accountName')]"
                ],
                "properties": {
                    "roleDefinitionId": "[variables('Azure Maps Data Reader')]",
                    "principalId": "[parameters('userAssignedIdentityPrincipalId')]",
                    "principalType": "ServicePrincipal"
                }
            },
            {
                "apiVersion": "2021-04-01-preview",
                "type": "Microsoft.KeyVault/vaults/secrets",
                "name": "[concat(parameters('keyVaultName'), '/', parameters('accountName'))]",
                "dependsOn": [
                    "[variables('accountId')]"
                ],
                "tags": {
                    "signingKey": "[variables('sasParameters').signingKey]",
                    "start" : "[variables('sasParameters').start]",
                    "expiry" : "[variables('sasParameters').expiry]"
                },
                "properties": {
                    "value": "[listSas(variables('accountId'), '2023-06-01', variables('sasParameters')).accountSasToken]"
                }
            }
        ]
    }
    
  7. Implante o modelo com os parâmetros de ID do Cofre da Chave e os recursos de identidade gerenciados criados na etapa anterior. Forneça o seu próprio valor para <group-name>. Ao criar o token SAS, defina o allowedRegions parâmetro como eastus, westus2e westcentralus. Em seguida, você pode usar esses locais para fazer solicitações HTTP para o ponto de us.atlas.microsoft.com extremidade.

    Importante

    Salve o token SAS no cofre de chaves para impedir que suas credenciais apareçam nos logs de implantação do Azure. O segredo do tags token SAS também contém o nome da chave de início, expiração e assinatura, para mostrar quando o token SAS expirará.

     az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./azuredeploy.json" --parameters keyVaultName="$($outputs[0])" userAssignedIdentityPrincipalId="$($outputs[1])" userAssignedIdentityResourceId="$($outputs[2])" allowedOrigins="['http://localhost']" allowedRegions="['eastus', 'westus2', 'westcentralus']" maxRatePerSecond="10"
    
  8. Localize e salve uma cópia do único segredo de token SAS do Cofre da Chave.

    $secretId = $(az keyvault secret list --vault-name $outputs[0] --query "[? contains(name,'map')].id" --output tsv)
    $sasToken = $(az keyvault secret show --id "$secretId" --query "value" --output tsv)
    
  9. Teste o token SAS fazendo uma solicitação para um ponto de extremidade do Azure Maps. Este exemplo especifica o para garantir que us.atlas.microsoft.com sua solicitação seja encaminhada para a geografia dos EUA. Seu token SAS permite regiões dentro da geografia dos EUA.

    az rest --method GET --url 'https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052' --headers "Authorization=jwt-sas $($sasToken)" --query "results[].address"
    

Exemplo de script completo

Para executar o exemplo completo, os seguintes arquivos de modelo devem estar no mesmo diretório da sessão atual do PowerShell:

  • prereq.azuredeploy.json para criar o cofre de chaves e a identidade gerenciada.
  • azuredeploy.json para criar a conta do Azure Maps, configurar a atribuição de função e a identidade gerenciada e armazenar o token SAS no cofre de chaves.
az login
az provider register --namespace Microsoft.KeyVault
az provider register --namespace Microsoft.ManagedIdentity
az provider register --namespace Microsoft.Maps

$id = $(az rest --method GET --url 'https://graph.microsoft.com/v1.0/me?$select=id' --headers 'Content-Type=application/json' --query "id")
az group create --name <group-name> --location "East US"
$outputs = $(az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./prereq.azuredeploy.json" --parameters objectId=$id --query "[properties.outputs.keyVaultName.value, properties.outputs.userAssignedIdentityPrincipalId.value, properties.outputs.userIdentityResourceId.value]" --output tsv)
az deployment group create --name ExampleDeployment --resource-group <group-name> --template-file "./azuredeploy.json" --parameters keyVaultName="$($outputs[0])" userAssignedIdentityPrincipalId="$($outputs[1])" userAssignedIdentityResourceId="$($outputs[2])" allowedOrigins="['http://localhost']" allowedRegions="['eastus', 'westus2', 'westcentralus']" maxRatePerSecond="10"
$secretId = $(az keyvault secret list --vault-name $outputs[0] --query "[? contains(name,'map')].id" --output tsv)
$sasToken = $(az keyvault secret show --id "$secretId" --query "value" --output tsv)

az rest --method GET --url 'https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052' --headers "Authorization=jwt-sas $($sasToken)" --query "results[].address"

Exemplo do mundo real

Você pode executar solicitações para APIs do Azure Maps da maioria dos clientes, como C#, Java ou JavaScript. O Postman converte uma solicitação de API em um trecho de código básico do cliente em praticamente qualquer linguagem de programação ou estrutura que você escolher. Você pode usar esse trecho de código gerado em seus aplicativos front-end.

O pequeno exemplo de código JavaScript a seguir mostra como você pode usar seu token SAS com a API de busca de JavaScript para obter e retornar informações do Azure Maps. O exemplo usa Get Search Address API versão 1.0. Forneça o seu próprio valor para <your SAS token>.

Para que este exemplo funcione, certifique-se de executá-lo de dentro da mesma origem que o allowedOrigins para a chamada de API. Por exemplo, se você fornecer https://contoso.com como o na chamada de API, a página HTML que hospeda o allowedOrigins script JavaScript deve ser https://contoso.com.

async function getData(url = 'https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052') {
  const response = await fetch(url, {
    method: 'GET',
    mode: 'cors',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'jwt-sas <your SAS token>',
    }
  });
  return response.json(); // parses JSON response into native JavaScript objects
}

postData('https://us.atlas.microsoft.com/search/address/json?api-version=1.0&query=1 Microsoft Way, Redmond, WA 98052')
  .then(data => {
    console.log(data); // JSON data parsed by `data.json()` call
  });

Clean up resources (Limpar recursos)

Quando não precisar mais dos recursos do Azure, você poderá excluí-los:

az group delete --name {group-name}

Próximos passos

Implante um modelo ARM de início rápido para criar uma conta do Azure Maps que usa um token SAS:

Para exemplos mais pormenorizados, consulte:

Encontre as métricas de uso da API para sua conta do Azure Maps:

Explore exemplos que mostram como integrar o Microsoft Entra ID com o Azure Maps: