做者:Allen
版权:转载请在文章明显位置注明做者及出处。如发现错误,欢迎批评指正。
距离上次分享关于 “Azure AD" 的知识过去差很少2个多月了, 今天最近恰好也是学习,分享一下关于Azure AD 使用多租户应用程序模式让任何 Azure Active Directory 用户登陆,以前仅仅都是在当初租户的用户或者受邀来宾来访问和使用咱们的api资源的。今天咱们将如下关于只要拥有微软 的工做/学校帐号的用户均可以使用咱们受AD保护的 API 资源。接下来就开始咱们今天的分享 html
--------------------我是分割线--------------------git
首先咱们登录Azure Portal 上,而且切换一下当前活动的目录(也就是当前所在的租户)github
在以前在AAD中注册好的应用注册---”WebApi“,点击进入WebApi的设置json
点击图中圈中的受支持的帐户类型---仅个人组织api
修改 受支持的帐号类型 为 ”任何组合目录(任何 Azure AD 目录 - 都租户)中的帐户“,点击 ”保存“app
咱们使用其余租户的帐号登录认证,提示 当前登录帐号不在当前登录的租户内ide
微软官方文档给出,当使用多租户模式的时候,post
(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 } }
咱们进行测试 order 接口,提示 返回码 401,无权限。
咱们点击页面上的 ”Authorize“ 进行验证
这里,咱们输入其余azure租户的用户的帐号信息进行登录验证(由于这号牵扯我的隐私,因此目前不展现),点击下一步
输入 帐号密码信息,点击登录
登录验证经过后,咱们再次进行验证操做
咱们再次进行测试,ok,成功
🎉🎉🎉🎉🎉!!! 成功!,你们能够都试试哈
今天的文章大概介绍了多租户模式登录/访问咱们的受Azure AD保护的api资源,以及经过 Swagger中使用隐式受权模式来访问Api资源。
代码稍等,我会整理一下,上传到github中
做者:Allen
版权:转载请在文章明显位置注明做者及出处。如发现错误,欢迎批评指正。