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单页面应用 ...
随机推荐
- Ribbon软负载 (F版)
Spring Cloud 为开发者提供了在分布式系统中的一些常用的组件(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁定,决策竞选,分布式会话集群状态).使用Sprin ...
- MySQL授权--WITH GRANT OPTION
今天在学习MySQL的时候,看到一句描述 WITH GRANT OPTION should be left out if the user need not be able to grant othe ...
- springMVC中的HttpSession与Model
目录 1.1 spring的@MODELATTRIBUTE 2.1 session的概念 3.1 示例 4.1 为什么springmvc框架要使用model这个对象呢? 突然发问:相信很多人在做WEB ...
- Python 简明教程 --- 21,Python 继承与多态
微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 程序不是年轻的专利,但是,它属于年轻. 目录 我们已经知道封装,继承和多态 是面向对象的三大特征,面 ...
- 【PyMuPDF和pdf2image】Python将PDF转成图片
前言: 在最近的测试中遇到一个与PDF相关的测试需求,其中有一个过程是将PDF转换成图片,然后对图片进行测试. 粗略的试了好几种方式,其中语言尝试了Python和Java,总体而言所找到的Python ...
- h5页面自动播放视频、音频_关于媒体文件自动全屏播放的实现方式
在移动端(ios和android)播放视频的时候,我们即使定义了autoplay属性,仍然不能自动播放.这是由于手机浏览器为了防止浪费用户的网络流量,在默认情况下是不允许媒体文件自动播放的,除非用户自 ...
- css图片居中,通过纯css实现图片居中的多种实现方法
在网页布局中,图文排版是我们常用的,那么经常会遇到如何让图片居中显示呢,这篇文章将总结常用css实现图片居中的方法总结: html结构: <div class="demo" ...
- springboot启动只显示图标不报错
问题如下: 问题原因是:logback.xml文件中日志打印级别设置的有问题.设置不打印
- Caocao's Bridges HDU - 4738 求桥
题目描述 Caocao was defeated by Zhuge Liang and Zhou Yu in the battle of Chibi. But he wouldn't give up. ...
- 二分查找&二叉排序树
首先我们先来复习一下二分查找的算法 对于正向序列的二分查找 递归实现: bool binary_search(vector<int> &sort_arry,int begin,in ...
