一,引言
距離上次分享關於 “Azure AD" 的知識過去差不多2個多月了, 今天最近剛好也是學習,分享一下關於Azure AD 使用多租戶應用程序模式讓任何 Azure Active Directory 用戶登錄,之前僅僅都是在當初租戶的用戶或者受邀來賓來訪問和使用我們的api資源的。今天我們將以下關於只要擁有微軟 的工作/學校賬號的用戶都可以使用我們受AD保護的 API 資源。接下來就開始我們今天的分享
--------------------我是分割線--------------------
1,Azure AD(一)入門認識
2,Azure AD(二)調用受Microsoft 標識平台保護的 ASP.NET Core Web API 上
3,Azure AD(二)調用受Microsoft 標識平台保護的 ASP.NET Core Web API 下
4,Azure AD(三)知識補充-Azure資源的托管標識
5,Azure AD(四)知識補充-服務主體
6,Azure AD(五)使用多租戶應用程序模式讓任何 Azure Active Directory 用戶登錄
二,正文
1,修改受保護資源的應用的賬號類型
首先我們登陸Azure Portal 上,並且切換一下當前活動的目錄(也就是當前所在的租戶)
在之前在AAD中注冊好的應用注冊---”WebApi“,點擊進入WebApi的設置
點擊圖中圈中的受支持的賬戶類型---僅我的組織
修改 受支持的賬號類型 為 ”任何組合目錄(任何 Azure AD 目錄 - 都租戶)中的賬戶“,點擊 ”保存“
我們使用其他租戶的賬號登陸認證,提示 當前登陸賬號不在當前登陸的租戶內
2,修改代碼配置
微軟官方文檔給出,當使用多租戶模式的時候,
(1)代碼需要更為為向/common 發出請求
在單租戶應用程序中,登錄請求將發送到租戶的登錄終結點。 以 trainingdiscussion.partner.onmschina.cn 為例,終結點將是:https://login.chinacloudapi.cn/trainingdiscussion.partner.onmschina.cn。 發送到租戶終結點的請求可以讓該租戶中的用戶(或來賓)登錄該租戶中的應用程序。
使用多租戶應用程序時,應用程序事先並不知道用戶來自哪個租戶,因此無法將請求發送到租戶的終結點。 取而代之的是,請求將發送到在所有 Azure AD 租戶之間多路復用的終結點:https://login.chinacloudapi.cn/common
當 Microsoft 標識平台在 /common 終結點上收到請求時,會使用戶登錄,因而可以發現用戶來自哪個租戶。 /Common 終結點可與 Azure AD 支持的所有身份驗證協議配合使用: OpenID Connect、OAuth 2.0、SAML 2.0 和 WS 聯合身份驗證。
/common 終結點不是租戶,也不是頒發者,而只是一個多路復用器。 使用 /common 時,需要更新應用程序中用於驗證令牌的邏輯。然后,對應用程序做出的登錄響應會包含代表該用戶的令牌。 令牌中的頒發者值告知應用程序該用戶來自哪個租戶。 從 /common 終結點返回響應時,令牌中的頒發者值將與用戶的租戶相對應。
(2)將代碼更新為處理多個頒發者值
單租戶應用程序通常采用類似於下面的終結點值:https://login.chinacloudapi.cn/trainingdiscussion.partner.onmschina.cn
並使用該值構造元數據 URL(在本例中為 OpenID Connect),例如:https://login.chinacloudapi.cn/trainingdiscussion.partner.onmschina.cn/.well-known/openid-configuration
以下載用於驗證令牌的兩項關鍵信息:租戶的簽名密鑰和頒發者值。 每個 Azure AD 租戶使用以下格式的唯一頒發者值:https://sts.chinacloudapi.cn/53359126-8bcf-455d-a934-5fe72d349207/
下圖中,是我當前AAD 租戶中注冊的 Web Api 的OpenID Connect 元數據文檔
Authentication 配置
services.AddAuthentication("Bearer") .AddJwtBearer(o => { o.Audience = Appsettings.app(new string[] { "AzureAD", "ClientId" }); o.RequireHttpsMetadata = false; o.SaveToken = true; o.Authority = Appsettings.app(new string[] { "AzureAD", "Authority" }); o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters() { ValidateIssuerSigningKey = true, ValidIssuer = Appsettings.app(new string[] { "AzureAD", "Issuer" }), ValidateLifetime = true, }; });
Swagger服務的配置
services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Description = "JWT授權(數據將在請求頭中進行傳輸) 直接在下框中輸入Bearer {token}(注意兩者之間是一個空格)\"", Type = SecuritySchemeType.OAuth2, In = ParameterLocation.Header,//jwt默認存放Authorization信息的位置(請求頭中) Flows = new OpenApiOAuthFlows() { Implicit = new OpenApiOAuthFlow { //AuthorizationUrl = new Uri($"https://login.chinacloudapi.cn/{ Appsettings.app(new string[] { "AzureAD", "TenantId" })}/oauth2/authorize") AuthorizationUrl = new Uri($"https://login.chinacloudapi.cn/common/oauth2/authorize") } } }); // 在header中添加token,傳遞到后台 c.OperationFilter<SecurityRequirementsOperationFilter>(); });
開啟中間件
#region Swagger app.UseSwagger(); app.UseSwaggerUI(c => { //根據版本名稱倒序 遍歷展示 var ApiName = Appsettings.app(new string[] { "Startup", "ApiName" }); c.SwaggerEndpoint($"/swagger/v1/swagger.json", $"{ApiName} v1"); c.OAuthClientId(Appsettings.app(new string[] { "Swagger", "ClientId" })); //c.OAuthClientSecret(Appsettings.app(new string[] { "Swagger", "ClientSecret" })); c.OAuthRealm(Appsettings.app(new string[] { "AzureAD", "ClientId" })); c.OAuthAppName("My API V1"); c.OAuthScopeSeparator(" "); c.OAuthAdditionalQueryStringParams(new Dictionary<string, string>() { { "resource", Appsettings.app(new string[] { "AzureAD", "ClientId" }) } }); }); #endregion IdentityModelEventSource.ShowPII = true; // here app.UseAuthentication();
完整代碼:

public class Startup { public Startup(IConfiguration configuration, IWebHostEnvironment environment) { Configuration = configuration; Environment = environment; } public IConfiguration Configuration { get; } public IWebHostEnvironment Environment { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddSingleton(new Appsettings(Environment.ContentRootPath)); //services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme) // .AddAzureADBearer(options => Configuration.Bind("AzureAd", options)); services.AddAuthentication("Bearer") .AddJwtBearer(o => { o.Audience = Appsettings.app(new string[] { "AzureAD", "ClientId" }); o.RequireHttpsMetadata = false; o.SaveToken = true; o.Authority = Appsettings.app(new string[] { "AzureAD", "Authority" }); o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters() { ValidateIssuerSigningKey = false, ValidIssuer = Appsettings.app(new string[] { "AzureAD", "Issuer" }), ValidateLifetime = true, }; }); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Description = "JWT授權(數據將在請求頭中進行傳輸) 直接在下框中輸入Bearer {token}(注意兩者之間是一個空格)\"", Type = SecuritySchemeType.OAuth2, In = ParameterLocation.Header,//jwt默認存放Authorization信息的位置(請求頭中) Flows = new OpenApiOAuthFlows() { Implicit = new OpenApiOAuthFlow { //AuthorizationUrl = new Uri($"https://login.chinacloudapi.cn/{ Appsettings.app(new string[] { "AzureAD", "TenantId" })}/oauth2/authorize") AuthorizationUrl = new Uri($"https://login.chinacloudapi.cn/common/oauth2/authorize") } } }); // 在header中添加token,傳遞到后台 c.OperationFilter<SecurityRequirementsOperationFilter>(); }); services.AddControllers(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } #region Swagger app.UseSwagger(); app.UseSwaggerUI(c => { //根據版本名稱倒序 遍歷展示 var ApiName = Appsettings.app(new string[] { "Startup", "ApiName" }); c.SwaggerEndpoint($"/swagger/v1/swagger.json", $"{ApiName} v1"); c.OAuthClientId(Appsettings.app(new string[] { "Swagger", "ClientId" })); //c.OAuthClientSecret(Appsettings.app(new string[] { "Swagger", "ClientSecret" })); c.OAuthRealm(Appsettings.app(new string[] { "AzureAD", "ClientId" })); c.OAuthAppName("My API V1"); c.OAuthScopeSeparator(" "); c.OAuthAdditionalQueryStringParams(new Dictionary<string, string>() { { "resource", Appsettings.app(new string[] { "AzureAD", "ClientId" }) } }); }); #endregion IdentityModelEventSource.ShowPII = true; // here app.UseAuthentication(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } }

{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "AzureAd": { "Instance": "https://login.chinacloudapi.cn/common", "Domain": "trainingdiscussion.partner.onmschina.cn", "TenantId": "common", "ClientId": "f38ec09d-203e-4b2d-a1c1-faf76a608528", "CallbackPath": "/signin-oidc", "Authority": "https://login.chinacloudapi.cn/organizations/v2.0/", "Issuer": "https://sts.chinacloudapi.cn/53359126-8bcf-455d-a934-5fe72d349207/" }, "Swagger": { "ClientId": "62ca9f31-585c-4d28-84b6-25fb7855aed0", "ClientSecret": "" // ?fxV/=/pwlRjwQgoIdLRlPNlWBBQ8939 } }
3,運行項目,進行測試
我們進行測試 order 接口,提示 返回碼 401,無權限。
我們點擊頁面上的 ”Authorize“ 進行驗證
這里,我們輸入其他azure租戶的用戶的賬號信息進行登陸驗證(因為這號牽扯個人隱私,所以目前不展示),點擊下一步
輸入 賬號密碼信息,點擊登陸
登陸驗證通過后,我們再次進行驗證操作
我們再次進行測試,ok,成功
🎉🎉🎉🎉🎉!!! 成功!,大家可以都試試哈
三,結尾
今天的文章大概介紹了多租戶模式登陸/訪問我們的受Azure AD保護的api資源,以及通過 Swagger中使用隱式授權模式來訪問Api資源。
代碼稍等,我會整理一下,上傳到github中
作者:Allen
版權:轉載請在文章明顯位置注明作者及出處。如發現錯誤,歡迎批評指正。