從Azure AD獲取用戶的組成員身份需要的不僅僅是「幾行代碼」,所以我認爲我會分享最終爲我工作的工作,以便爲其他人節省幾天的費用 - 拉和頭撞。
首先,讓我們通過添加下列依賴於project.json:
"dependencies": {
...
"Microsoft.IdentityModel.Clients.ActiveDirectory": "3.13.8",
"Microsoft.Azure.ActiveDirectory.GraphClient": "2.0.2"
}
第一個是必要的,因爲我們需要爲了驗證我們的應用程序爲它能夠訪問AAD圖形API。 第二個是我們將用來查詢用戶成員資格的Graph API客戶端庫。 不言而喻,這些版本僅在撰寫本文時有效,並可能在未來發生變化。
接下來,啓動類的配置()方法,也許我們配置ID連接的身份驗證之前,我們創建了圖形API客戶端如下:
var authContext = new AuthenticationContext("https://login.microsoftonline.com/<your_directory_name>.onmicrosoft.com");
var clientCredential = new ClientCredential("<your_b2c_app_id>", "<your_b2c_secret_app_key>");
const string AAD_GRAPH_URI = "https://graph.windows.net";
var graphUri = new Uri(AAD_GRAPH_URI);
var serviceRoot = new Uri(graphUri, "<your_directory_name>.onmicrosoft.com");
this.aadClient = new ActiveDirectoryClient(serviceRoot, async() => await AcquireGraphAPIAccessToken(AAD_GRAPH_URI, authContext, clientCredential));
警告:不要硬編碼的祕密的應用程序密鑰,但保持在一個安全的地方。那麼,你已經知道了,對吧? :)
當客戶端需要獲取身份驗證令牌時,我們提交給AD客戶端構造函數的異步AcquireGraphAPIAccessToken()方法將根據需要進行調用。下面就是該方法的樣子:
private async Task<string> AcquireGraphAPIAccessToken(string graphAPIUrl, AuthenticationContext authContext, ClientCredential clientCredential)
{
AuthenticationResult result = null;
var retryCount = 0;
var retry = false;
do
{
retry = false;
try
{
// ADAL includes an in-memory cache, so this will only send a request if the cached token has expired
result = await authContext.AcquireTokenAsync(graphAPIUrl, clientCredential);
}
catch (AdalException ex)
{
if (ex.ErrorCode == "temporarily_unavailable")
{
retry = true;
retryCount++;
await Task.Delay(3000);
}
}
} while (retry && (retryCount < 3));
if (result != null)
{
return result.AccessToken;
}
return null;
}
注意,它具有處理瞬態條件下,您可能要調整到應用程序的需求,內置重試機制。
現在我們已經關注了應用程序認證和AD客戶端設置,我們可以繼續使用OpenIdConnect事件來最終使用它。 早在那裏我們會通常要求app.UseOpenIdConnectAuthentication()
創造OpenIdConnectOptions實例的配置()方法中,我們添加一個事件處理程序OnTokenValidated事件:
new OpenIdConnectOptions()
{
...
Events = new OpenIdConnectEvents()
{
...
OnTokenValidated = SecurityTokenValidated
},
};
該事件被觸發時,訪問令牌的signing-已經獲得,驗證並建立了用戶身份。 (不要與調用AAD Graph API所需的應用程序自己的訪問令牌混淆!) 它看起來像是查詢用戶組成員資格的Graph API的好地方,並以附加聲明的形式將這些組添加到標識中:
private Task SecurityTokenValidated(TokenValidatedContext context)
{
return Task.Run(async() =>
{
var oidClaim = context.SecurityToken.Claims.FirstOrDefault(c => c.Type == "oid");
if (!string.IsNullOrWhiteSpace(oidClaim?.Value))
{
var pagedCollection = await this.aadClient.Users.GetByObjectId(oidClaim.Value).MemberOf.ExecuteAsync();
do
{
var directoryObjects = pagedCollection.CurrentPage.ToList();
foreach (var directoryObject in directoryObjects)
{
var group = directoryObject as Group;
if (group != null)
{
((ClaimsIdentity)context.Ticket.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, group.DisplayName, ClaimValueTypes.String));
}
}
pagedCollection = pagedCollection.MorePagesAvailable ? await pagedCollection.GetNextPageAsync() : null;
}
while (pagedCollection != null);
}
});
}
此處使用的是角色聲明類型,但您可以使用自定義聲明類型。
做完上面的,如果你使用ClaimType.Role,所有你需要做的是裝飾你的控制器類或方法,像這樣:
[Authorize(Role = "Administrators")]
那是當然的,只要你有一在B2C中配置了一個顯示名稱爲「管理員」的指定組。
但是,如果您選擇使用自定義聲明類型,你需要通過在ConfigureServices加入這樣的事情()方法,定義基於聲明類型的授權策略,例如:
services.AddAuthorization(options => options.AddPolicy("ADMIN_ONLY", policy => policy.RequireClaim("<your_custom_claim_type>", "Administrators")));
再裝飾一個特權控制器類或方法如下:
[Authorize(Policy = "ADMIN_ONLY")]
好,是我們做了嗎? - 呃,不完全是。
如果您運行了應用程序並嘗試登錄,則會從Graph API中得到一個異常,聲稱「權限不足以完成操作」。 這可能並不明顯,但是當您的應用程序使用其app_id和app_key成功驗證AD時,它不具備從AD讀取用戶詳細信息所需的權限。 爲了授予應用程序,訪問,我選擇使用Azure Active Directory Module for PowerShell
下面的腳本的伎倆對我來說:
$tenantGuid = "<your_tenant_GUID>"
$appID = "<your_app_id>"
$userVal = "<admin_user>@<your_AD>.onmicrosoft.com"
$pass = "<admin password in clear text>"
$Creds = New-Object System.Management.Automation.PsCredential($userVal, (ConvertTo-SecureString $pass -AsPlainText -Force))
Connect-MSOLSERVICE -Credential $Creds
$msSP = Get-MsolServicePrincipal -AppPrincipalId $appID -TenantID $tenantGuid
$objectId = $msSP.ObjectId
Add-MsolRoleMember -RoleName "Company Administrator" -RoleMemberType ServicePrincipal -RoleMemberObjectId $objectId
而現在我們終於完成了! 「幾行代碼」的情況如何? :)
你能否顯示那些5-6行?我一直在努力拼湊這個問題的答案几天,現在我已經有超過100行代碼(並且它還沒有工作!)。如果像5或6行那樣簡單地連接通知,查詢用戶組數據的圖形,並將這些組添加到ClaimsPrincipal角色,我顯然會咆哮錯誤的樹。我真的很感激一些重定向! – reidLinden