获取 "服务器未能验证该请求。确保授权头的值正确形成,包括签名。" 在尝试使用C#语言在Azure中使用系统分配的托管身份时出现错误。
遵循的步骤是
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
at Microsoft.Azure.Storage.Core.Executor.Executor.<ExecuteAsync>d__1`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Azure.Storage.Blob.CloudBlockBlob.<DownloadTextAsync>d__72.MoveNext()
程序类
class Program
{
static void Main(string[] args)
{
try
{
var blob = new AzureCloudBlob();
Console.WriteLine(blob.ReadBlob());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
}
}
AzureCloudBlob类,用于使用系统分配的托管身份访问令牌连接和读取blob。
class AzureCloudBlob
{
public CloudBlockBlob CreateConnection()
{
var token = GetToken();
var tokenCredentials = new TokenCredential(token);
var storageCredentials = new StorageCredentials(tokenCredentials);
var serviceUri = new Uri("https://mystorageacc.blob.core.windows.net/practice/blob.txt");
return new CloudBlockBlob(serviceUri, storageCredentials);
}
public string ReadBlob()
{
var client = CreateConnection();
var blobClient = client.DownloadTextAsync();
var data = blobClient.Result;
return data;
}
public string GetToken()
{
var request = (HttpWebRequest)WebRequest.Create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/");
request.Headers["Metadata"] = "true";
request.Method = "GET";
try
{
var response = (HttpWebResponse)request.GetResponse();
var streamResponse = new StreamReader(response.GetResponseStream());
string stringResponse = streamResponse.ReadToEnd();
var list = (Dictionary<string, string>)JsonConvert.DeserializeObject(stringResponse, typeof(Dictionary<string, string>));
string accessToken = list["access_token"];
Console.WriteLine(accessToken);
return accessToken;
}
catch (Exception e)
{
string errorText = String.Format("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : "Acquire token failed");
Console.WriteLine(errorText);
return errorText;
}
}
}
如果你想使用Azure AD auth访问Azure存储,我们应该使用 resouce=https://storage.azure.com/
来获取Azure AD访问令牌。但你使用 resource=https://management.azure.com/
. 请更换。此外,请注意,您需要为MSI分配正确的角色。角色应该是 储存Blob数据读取器, 存储Blob数据贡献者 或 存储Blob数据所有者.
具体步骤如下
在 Azure 虚拟机上启用系统分配的托管身份。
$vm = Get-AzVM -ResourceGroupName myResourceGroup -Name myVM
Update-AzVM -ResourceGroupName myResourceGroup -VM $vm -AssignIdentity:$SystemAssigned
在存储账户范围内为MSI分配角色。
$sp =Get-AzADServicePrincipal -displayname "<your VM name>"
New-AzRoleAssignment -ObjectId $sp.id `
-RoleDefinitionName "Storage Blob Data Reader" `
-Scope "/subscriptions/<subscription>/resourceGroups/sample-resource-group/providers/Microsoft.Storage/storageAccounts/<storage-account>"
编码
class Program
{
static void Main(string[] args)
{
//get token
string accessToken = GetMSIToken("https://storage.azure.com/");
//create token credential
TokenCredential tokenCredential = new TokenCredential(accessToken);
//create storage credentials
StorageCredentials storageCredentials = new StorageCredentials(tokenCredential);
Uri blobAddress = new Uri("<URI to blob file>");
//create block blob using storage credentials
CloudBlockBlob blob = new CloudBlockBlob(blobAddress, storageCredentials);
//retrieve blob contents
Console.WriteLine(blob.DownloadText());
Console.ReadLine();
}
static string GetMSIToken(string resourceID)
{
string accessToken = string.Empty;
// Build request to acquire MSI token
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=" + resourceID);
request.Headers["Metadata"] = "true";
request.Method = "GET";
try
{
// Call /token endpoint
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
// Pipe response Stream to a StreamReader, and extract access token
StreamReader streamResponse = new StreamReader(response.GetResponseStream());
string stringResponse = streamResponse.ReadToEnd();
JavaScriptSerializer j = new JavaScriptSerializer();
Dictionary<string, string> list = (Dictionary<string, string>)j.Deserialize(stringResponse, typeof(Dictionary<string, string>));
accessToken = list["access_token"];
return accessToken;
}
catch (Exception e)
{
string errorText = String.Format("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : "Acquire token failed");
return accessToken;
}
}
}