Azure AD(五)使用多租户应用程序模式让任何 Azure Active Directory 用户登录
一,引言
距离上次分享关于 “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();
});
}
}
Startup

{
"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
}
}
application
3,运行项目,进行测试
我们进行测试 order 接口,提示 返回码 401,无权限。

我们点击页面上的 ”Authorize“ 进行验证

这里,我们输入其他azure租户的用户的账号信息进行登陆验证(因为这号牵扯个人隐私,所以目前不展示),点击下一步

输入 账号密码信息,点击登陆

登陆验证通过后,我们再次进行验证操作

我们再次进行测试,ok,成功

!!! 成功!,大家可以都试试哈
三,结尾
今天的文章大概介绍了多租户模式登陆/访问我们的受Azure AD保护的api资源,以及通过 Swagger中使用隐式授权模式来访问Api资源。
代码稍等,我会整理一下,上传到github中
作者:Allen
版权:转载请在文章明显位置注明作者及出处。如发现错误,欢迎批评指正。
Azure AD(五)使用多租户应用程序模式让任何 Azure Active Directory 用户登录的更多相关文章
- Azure AD B2C(二)使用Azure AD B2C为ASP.NET Core 应用设置社交帐户(邮箱)登录/注册
一,引言 上次关于Azure AD B2C 讲到一些概念,有介绍到,Azure AD B2C 也是一种身份验证的解决方案,但是它运行客户使用其首选的社交,企业或者本地账户标识对应用程序和API进行单一 ...
- Windows Azure Virtual Machine (38) 跨租户迁移使用托管磁盘的Azure虚拟机
<Windows Azure Platform 系列文章目录> 背景介绍: (1)我们建议使用Azure Manage Disk托管磁盘来创建Azure虚拟机 (2)使用托管磁盘的好处是, ...
- AD域的安装(在Windows Server 2003中安装Active Directory)
在Active Directory中提供了一组服务器作为身份验证服务器或登录服务器,这类服务器被称作域控制器(Domain Controller,简称DC).建立一个AD域的过程实际就是在一台运行Wi ...
- 编写Java程序,使用Swing事件处理机制实现用户登录和英雄信息显示
返回本章节 返回作业目录 需求说明: 使用Swing事件处理机制实现用户登录和英雄信息显示 实现思路: 创建LoginView类,该类用于显示登录界面,为登录按钮添加ActionListener事件, ...
- Azure AD(六)添加自定义域名
一,引言 每当我们在 Azure Portal 上创建新的租户时,都会在设置租户的 "初始域名" 后加上 ".onmicrosoft.com",默认情况下 &q ...
- 在 Windows Azure 上设计多租户应用程序
作者:Suren Machiraju 和 Ralph Squillace 审校:Christian Martinez.James Podgorski.Valery Mizonov 和 Michael ...
- 无责任Windows Azure SDK .NET开发入门(二):使用Azure AD 进行身份验证
<編者按>本篇为系列文章,带领读者轻松进入Windows Azure SDK .NET开发平台.本文为第二篇,将教导读者使用Azure AD进行身分验证.也推荐读者阅读无责任Windows ...
- Azure AD(一)入门认识
一,引言(吹水) 距离上一次介绍Azure Functions的相关博文以及过期快一个月了,本来早早都想好已经规划好的Azure的相关的学习的路线,无奈还是由于自己文笔不好以及自身太懒,导致博文没有更 ...
- Azure AD(二)调用受Microsoft 标识平台保护的 ASP.NET Core Web API 上
一,引言 上一节讲到Azure AD的一些基础概念,以及Azure AD究竟可以用来做什么?本节就接着讲如何在我们的项目中集成Azure AD 包含我们的API资源(其实这里还可以在 SPA单页面应用 ...
随机推荐
- 致Spring Boot初学者
1.引言 Spring Boot是近两年来火的一塌糊涂,来这里的每一位同学,之前应该大致上学习了web项目开发方面的知识,正在努力成长过程中.因为最近有不少人来向我“请教”,他们大都是一些刚入门的新手 ...
- 超简洁!利用easyExcel导出,读入Excel
x 深夜,在东莞,7天酒店,打开电脑,访问国内最大的同性交友网站. 日常开发中,导出导入场景非常多,尤其是对于后台管理更是一个列表一个导出,如果从导出的业务中抽 ...
- 大多数人可能都不会使用socketTimeout,看了底层才知道一直都做错了
前几天一个机房网络抖动,引发了很多对外请求的超时问题,在发生问题排查日志的时候,发现了这么一个现象,httpclient我们的请求超时时间并没有按照我们的设置报超时异常 我们的大概配置如下: Requ ...
- MySQL的数据类型 及注意事项
MySQL的数据类型 注意 选择合适的数据类型,能节省储存空间,提升计算性能.(1)在符合应用要求(取值范围.精度)的前提下,尽量使用“短”数据类型(2)数据类型越简单越好.(3)在MySQL中, ...
- HotSpot的启动过程
HotSpot通常会通过java.exe或javaw.exe来调用/jdk/src/share/bin/main.c文件中的main()函数来启动虚拟机,使用Eclipse进行调试时,也会调用到这个入 ...
- 苹果手机history.back()返回不刷新问题
苹果手机,a页面打开b页面,b页面使用history.back(-1)返回a页面时,a页面不刷新,可在a页面添加以下代码: var isPageHide = false; window.addEven ...
- 小师妹学JVM之:Dirty cards和PLAB
目录 简介 分代收集器中的空间划分 Write barrier和Dirty cards PLAB old space分配对象 总结 简介 分代垃圾回收器在进行minor GC的时候会发生什么操作呢?有 ...
- Window - 安装 ant
官方下载地址 https://ant.apache.org/bindownload.cgi 旧版下载地址 https://archive.apache.org/dist/ant/binaries/ 挑 ...
- 题解:2018级算法第六次上机 C6-不Nan的过河
题目描述: 样例: 实现解释: 一道因为没排序做了一个小时没做出来的二分答案模板题(手动呲牙) 知识点: 二分答案,最大值最小化 坑点: 排序,judge(mid)函数内计数的实现 其实从最长一步的最 ...
- Go Pentester - HTTP CLIENTS(3)
Interacting with Metasploit Early-stage Preparation: Setting up your environment - start the Metaspl ...
