我正在學習 Les Jackson 的The Complete ASP.NET Core 3 API Tutorial,但被卡住了。我在第 14 章“保護我們的 API”。
在本章中,我們應該做的是配置我們構建的 API 以接受來自 Azure Active Directory 的 JWT 令牌,然后構建一個客戶端,該客戶端從 Azure Active Directory 獲取 JWT 令牌,然后將其作為針對 API 的呼叫中的自定義標頭。
此時,我們仍在 localhost 上運行 API,接下來將它托管在 Azure 上。
在所有這些中,我在 Azure 中使用我的默認目錄。
將 Azure AD JWT 添加到 API:
本章列出了以下步驟:
- 在 Azure AD 中注冊我們的 API
- 在 Azure 中公開我們的 API
- 更新我們的 API 清單
- 添加額外的配置元素
- 添加新的包參考
- 更新API專案原始碼
我已經完成了 #1 和 #2,當我查看我的 API 時,我看到:
Display name: CommandAPI_DEV
Application (client) ID: 1e994557-5ae1-47bf-8ab7-b0ce2f8f3852
Object ID: b8225518-eba3-4a6a-8c30-ae82095a4ba7
Directory (tenant) ID: 9f9fbb85-6a89-4fac-a52a-845135fbe887
Application ID URI: api://1e994557-5ae1-47bf-8ab7-b0ce2f8f3852
Managed application in local directory: CommandAPI_DEV
對于#3,我已添加到清單中:
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"description": "Daemon apps in this role can consume the web api.",
"displayName": "DaemonAppRole",
"id": "be111a2a-ea62-47a9-8f55-5d8f84af3276",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "DaemonAppRole"
}
],
對于 #4,我創建了以下用戶機密:
{
"UserID": "cmddbuser",
"TenantId": "9f9fbb85-6a89-4fac-a52a-845135fbe887",
"ResourceId": "app://1e994557-5ae1-47bf-8ab7-b0ce2f8f3852",
"Password": "pa55w0rd!",
"Instance": "https://login.microsoftonline.com/",
"Domain": "jdegejdege.onmicrosoft.com",
"ClientId": "1e994557-5ae1-47bf-8ab7-b0ce2f8f3852"
}
#5 只是添加 NuGet 包。對于 #6,我已添加到 ConfigureServices():
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opt =>
{
opt.Audience = Configuration["ResourceId"];
opt.Authority = $"{Configuration["Instance"]}{Configuration["TenantId"]}";
})
;
請注意,此 opt.Audience 為:
"app://1e994557-5ae1-47bf-8ab7-b0ce2f8f3852"
并選擇授權:
"https://login.microsoftonline.com/9f9fbb85-6a89-4fac-a52a-845135fbe887"
這些值存盤在 User Secrets 中,但從 Azure AD 上應用程式頁面的“Essentials”部分復制。
然后我添加到配置():
app.UseAuthentication();
app.UseAuthorization();
我已經添加[Authorize]到我的控制器端點之一。
有了所有這些,API 專案的一切作業正常。
構建客戶端:
本章列出了以下步驟:
- 在 Azure AD 中注冊客戶端應用程式
- 在 Azure 中創建客戶端機密
- 配置客戶端 API 權限
- 撰寫我們的客戶端應用程式
So I did #1, and when I view the application in Azure AD I see:
Display name: CommandAPI_Client_DEV
Application (client) ID: d32007a5-642d-413a-82d9-4761e3030890
Object ID: 94d13c40-5556-4e12-aa2c-be9619681712
Directory (tenant) ID: 9f9fbb85-6a89-4fac-a52a-845135fbe887
Application ID URI: Add an Application ID URI
Managed application in local directory: CommandAPI_Client_DEV
Then I did #2, creating a client secret. When I did, the UI displayed three fields:
Description: CommandAPI_Client_DEV_secret
Value: NTK*****************************
Secret ID: 954fa788-c8b8-4265-97d7-a1bd83be3bcf
(When I created the secret, it displayed 37 characters. When I go back I just see the first three followed by asterisks.)
Then for #3, I went into API permissions, added a new permission to my API, then granted admin consent.
I did not get a Microsoft authentication popup, but the Azure UI showed the permission's status as "Granted for Default Directory".
For #4 I created a simple console app, putting this in appsettings.json:
{
"Instance": "https://login.microsoftonline.com/{0}",
"TenantId": "9f9fbb85-6a89-4fac-a52a-845135fbe887",
"ClientId": "d32007a5-642d-413a-82d9-4761e3030890",
"ClientSecret": "NTK*****************************",
"BaseAddress": "https://localhost:5001/api/Commands/1",
"ResourceId": "api://1e994557-5ae1-47bf-8ab7-b0ce2f8f3852/.default"
}
Note that "TenantId" matches "Directory (tenant) ID" from the client app's configuration in Azure AD.
"ClientId" matches "Application (client) ID" from the client app's configuration in Azure AD.
"ClientSecret" matches the value field I was given when I created the secret.
And "ResourceId" matches the "Application ID URI" field from the API's configuration in Azure AD.
The client app, then, is pretty straightforward.
Main calls RunAsync():
static void Main(string[] args)
{
Console.WriteLine("Making the call...");
RunAsync().GetAwaiter().GetResult();
}
private static async Task RunAsync()
{
AuthConfig is a simple wrapper for our settings in appsettings.json. config.Authority is
AuthConfig config = AuthConfig.ReadFromJsonFile("appsettings.json");
var authority = String.Format(CultureInfo.InstalledUICulture, config.Instance, config.TenantId);
Console.WriteLine($"Authority: {authority}");
We call into Azure AD to get our JWT:
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder
.Create(config.ClientId)
.WithClientSecret(config.ClientSecret)
.WithAuthority(new Uri(authority))
.Build();
string[] ResourceIds = new string[] { config.ResourceId };
AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(ResourceIds).ExecuteAsync();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Token acquired\n");
Console.WriteLine(result.AccessToken);
Console.ResetColor();
}
catch (MsalClientException ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(ex.Message);
Console.ResetColor();
}
if (!string.IsNullOrEmpty(result.AccessToken))
{
Then we set the JWT as a bearer token:
var httpClient = new HttpClient();
var defaultRequestHeaders = httpClient.DefaultRequestHeaders;
if (defaultRequestHeaders.Accept == null ||
!defaultRequestHeaders.Accept.Any(m => m.MediaType == "application/json"))
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
defaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);
And call the secure endpoint of our API:
HttpResponseMessage response = await httpClient.GetAsync(config.BaseAddress);
if (response.IsSuccessStatusCode)
{
Console.ForegroundColor = ConsoleColor.Green;
string json = await response.Content.ReadAsStringAsync();
Console.WriteLine(json);
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"Failed to call the Web API: {response.StatusCode} ");
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Content: {content}");
}
Console.ResetColor();
}
}
And when I do I get a valid JWT, which decodes as:
{
"typ": "JWT",
"alg": "RS256",
"x5t": "l3sQ-50cCH4xBVZLHTGwnSR7680",
"kid": "l3sQ-50cCH4xBVZLHTGwnSR7680"
}.{
"aud": "api://1e994557-5ae1-47bf-8ab7-b0ce2f8f3852",
"iss": "https://sts.windows.net/9f9fbb85-6a89-4fac-a52a-845135fbe887/",
"iat": 1636246957,
"nbf": 1636246957,
"exp": 1636250857,
"aio": "E2ZgYLhnIvUl2 2Z4Jno3ebX7pVvAAA=",
"appid": "d32007a5-642d-413a-82d9-4761e3030890",
"appidacr": "1",
"idp": "https://sts.windows.net/9f9fbb85-6a89-4fac-a52a-845135fbe887/",
"oid": "cc6633e2-df64-41be-bbb2-de750766e40a",
"rh": "0.AUYAhbufn4lqrE-lKoRRNfvoh6UHINMtZDpBgtlHYeMDCJCAAAA.",
"roles": [
"DaemonAppRole"
],
"sub": "cc6633e2-df64-41be-bbb2-de750766e40a",
"tid": "9f9fbb85-6a89-4fac-a52a-845135fbe887",
"uti": "tgVrQAPepkiMGHO04uHdAA",
"ver": "1.0"
}.[Signature]
But when I make the call against the API, httpClient.GetAsync() returns 401, with the WwwAuthenticate header set to:
{
Bearer error="invalid_token",
error_description="The audience 'api://1e994557-5ae1-47bf-8ab7-b0ce2f8f3852' is invalid"
}
Obviously I'm doing something wrong, but what?
uj5u.com熱心網友回復:
問題似乎是令牌問題將aud欄位設定"api://1e994557-5ae1-47bf-8ab7-b0ce2f8f3852"為什么與您的安全 API 期望受眾是什么之間的不匹配,即"app://1e994557-5ae1-47bf-8ab7-b0ce2f8f3852".
您需要api...在構建安全 API 的第 4 步中將受眾設定為使其作業。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/351257.html
標籤:c# azure authentication azure-active-directory jwt
上一篇:如何將ProjectExpression與帶空格的列名一起使用?
下一篇:動態變數做空結果
