【壹刊】Azure AD(二)调用受Microsoft 标识平台保护的 ASP.NET Core Web API (上)

—————————Grant_Allen 是一位博客园新晋博主,目前开始专注于Azure方向的学习和研究,是我认识不多的、打算长时间研究Azure的群友,因此打算帮他开个专栏,同时也希望并祝愿他能一直坚持下去,学有所成。

正文

一,引言

  上一节讲到Azure AD的一些基础概念,以及Azure AD究竟可以用来做什么?本节就接着讲如何在我们的项目中集成Azure AD 包含我们的API资源(其实这里还可以在 SPA单页面应用,Web项目,移动/桌面应用程序集成Azure AD),号了,废话不多说,开始今天的内容。

二,正文

上一篇介绍到 Azure AD 其实是微软基于云的表示和授权访问管理服务,它可以帮助我们在Azure中登录和访问资源。我们可以通过Azure的标识平台生成应用程序,采用微软表示登录,以及获取令牌来调用受保护的API资源。也就是说这一切功能也是基于包含Oauth 2.0和Open ID Connect的身份验证服务。

下面先去了解,熟悉一下关于Identity Server 4的 OpenID 和 OAuth 的区别以及授权模式

如果之前有了解 Identity Server 4 这种授权验证的框架,可以跳过下面的介绍:

identityServer4 知多少(圣杰):
https://www.cnblogs.com/sheng-jie/p/9430920.html

授权服务器identityServer4 开篇(老张的哲学):
https://www.cnblogs.com/laozhang-is-phi/p/10483922.html

(一) OpenID 和 OAuth 的区别 (以下的介绍来自google和 OAuth官网)

  1,OpenID 是一个以用户为中心的数字身份识别框架,它具有开放、分散性。OpenID 的创建基于这样一个概念:我们可以通过 URI (又叫 URL 或网站地址)来认证一个网站的唯一身份,简单通俗的理解,OpenID是用来做为身份验证的

  2,OAuth 2.0是用于授权的行业标准协议。OAuth 2.0致力于简化客户端开发人员的工作,同时为Web应用程序,桌面应用程序,移动电话和客厅设备提供特定的授权流程。也就是说 OAuth 2.0 是用来进行授权的

  3,OpenID Connect 是基于OAuth 协议的简单身份层。它允许客户端基于授权服务器执行的身份验证来验证最终用户的身份,并以可互操作且类似于REST的方式获取有关最终用户的基本配置文件信息。OpenID Connect允许所有类型的客户端(包括基于Web的客户端,移动客户端和JavaScript客户端)请求并接收有关经过身份验证的会话和最终用户的信息。规范套件是可扩展的,允许参与者在对他们有意义的时候使用可选功能,例如身份数据加密,OpenID提供程序的发现以及会话管理。

  OpenID Connect执行许多与OpenID 2.0相同的任务,但是这样做的方式是API友好的,并且可由本机和移动应用程序使用,OpenID Connect定义了用于可靠签名和加密的可选机制。OAuth 1.0a和OpenID 2.0的集成需要扩展,而在OpenID Connect中,OAuth 2.0功能与协议本身集成在一起。

(二)授权模式

  1,隐式模式(Implicit Flow)

  2,客户端授权模式(Client Credentials Flow)

  3,授权码授权模式(Authorization Code Flow)

  4,资源持有者密码模式(Resource Owner Password Credentials ):注意一下,这里的密码翻译的不正确,应该是单单指密码,证书也是可以的

  。。。。。等

这里暂时只了解这四种常见的授权模式。

(三)添加受保护资源

1,VS 创建 “Asp.Net Core WebApi” 项目,并且添加 “OrderController” 控制器,并且新增相应的方法,此步骤暂时省略,详细代码我整理完成后,会添加到github上。

2,安装 nuget:
Microsoft.AspNetCore.Authentication.AzureAD.UI

3,需要注册验证服务,整个地方默认的是 “AzureADJwtBearer”,AddAzureADBearer方法绑定Azure AD身份验证终结点,租户,租户所在的自定义域,以及客户端Id

services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme).AddAzureADBearer(options => Configuration.Bind("AzureAd", options));

开启Authentication中间件

// open authentication middleware
app.UseAuthentication();

4,在Azure Portal 上添加一个租户

  4.1 在Azure Portal 上选择 菜单 “Azure Active Directory”

              

  4.2,点击图中的 “创建目录”

      

  

4.3,目录选择默认 “Azure Active Directory”,点击 “下一步-配置”

         

  4.4,添加对应的组织名称和初始域名,

    组织名:myCommpany

    初始域名:trainingdiscussion 

   点击 “产看+创建” 进行验证,验证完成后点击 “创建” 

5,注册 “应用程序”

  5.1,Azure Portal 点击个人头像,切换目录

         

  5.2,选中刚刚创建的 “MyCompany” 的目录,继续在Portal首页左侧选择 “Azure Active Directory”,选中 “应用注册” ,点击 “新注册”    

  5.3,填写应用注册的一些基本信息

    (1)添加受保护的Api资源的名称,也就是我们在VS中创建的.Net Core 的 WebApi 项目,我这里暂时命名为 “WebApi”,

    (2)选择支持的账户类型,我这里选择的是一个多租户的类型

    (3)平台配置,选择 Web API,这里的平台配置怎么理解:就好在Web项目中是在成功验证用户身份后,会携带令牌,我们作为目标接受的URL,称其为 ”回调地址“

           

5.4, 点击 ”注册“,然后选择 ”管理“---》”身份验证“,点击”切换到旧体验“

5.5,找到隐式授权模式,勾选 ”访问令牌“,”ID令牌“两个复选框

             

  OK,以上我们在Azure Portal 就配置好一个客户端的注册,

5.6,在此,我们真正在代码中开启验证的话,还需要4个参数,也就是上面提到的 ”自定义域(Domain)“,”租户Id(TenantId)“,”客户端Id(ClientId)“,”应用注册终结点(Instance)“

  (1)Domain,TenantId (Domain 参数可以在创建目录时,先行复制好)

    

(2)ClientId:选择刚刚注册好的应用程序,进入应用程序页面后,找到对象Id 进行复制操作。

  

 

  (3)Instance:每个国家都有一个单独的Azure门户。若要在应用程序中与Azure AD进行集成,需要在每个特定环境的Azure门户中单独注册应用程序。

    所有用于验证应用程序的Azure AD终结点的URL也是不同的

适用于美国政府的 Azure AD  :
https://login.microsoftonline.us

Azure AD 德国:
https://login.microsoftonline.de

由世纪互联运营的 Azure AD 中国:
https://login.chinacloudapi.cn

Azure AD(全球服务) :
https://login.microsoftonline.com

 

  例如,对于 Azure 中国:

  • 授权常用终结点为:
    https://login.chinacloudapi.cn/common/oauth2/authorize

  • 令牌常用终结点为 :
    https://login.chinacloudapi.cn/common/oauth2/token

对于单租户应用程序,请将先前 URL 中的“common”替换为你的租户 ID 或名称。 示例为
https://login.chinacloudapi.cn/myCommany。

6,配置文件中的内容如下所示

"AzureAd": {"Instance": "https://login.chinacloudapi.cn/","Domain": "trainingdiscussion.partner.onmschina.cn","TenantId": "53359126-8bcf-455d-a934-5fe72d349207","ClientId": "f38ec09d-203e-4b2d-a1c1-faf76a608528"},

给需要验证的方法或者控制器加上验证标签[Authorize]

详情请看完整代码

完整代码:

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.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{Scopes = new Dictionary<string, string>{{ "user_impersonation", "Access API" }},AuthorizationUrl = new Uri($"https://login.chinacloudapi.cn/{ Appsettings.app(new string[] { "AzureAD", "TenantId" })}/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 Swaggerapp.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// open authentication middlewareapp.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/","Domain": "trainingdiscussion.partner.onmschina.cn","TenantId": "53359126-8bcf-455d-a934-5fe72d349207","ClientId": "f38ec09d-203e-4b2d-a1c1-faf76a608528"},"Swagger": {"ClientId": "e15070c3-7e9a-40c0-b73f-2f34fb031641","ClientSecret": "" //  ?fxV/=/pwlRjwQgoIdLRlPNlWBBQ8939}
}
[Route("api/[controller]")][ApiController]public class OrderController : ControllerBase{// GET: api/Order[HttpGet][Authorize]public IEnumerable<string> Get(){return new string[] { "value1", "value2" };}// GET: api/Order/5[HttpGet("{id}", Name = "Get")]public string Get(int id){return "value";}// POST: api/Order[HttpPost]public void Post([FromBody] string value){}// PUT: api/Order/5[HttpPut("{id}")]public void Put(int id, [FromBody] string value){}// DELETE: api/ApiWithActions/5[HttpDelete("{id}")]public void Delete(int id){}}

7,项目添加Swagger的配置,使用Swagger进行接口测试-

  7.1:安装  Swashbuckle.AspNetCore

  7.1:配置 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")}}});// 在header中添加token,传递到后台c.OperationFilter<SecurityRequirementsOperationFilter>();});

7.3,开启中间件

 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" }) } });});

详细代码,请看上面的的完整代码☝☝☝☝☝

  7.4,注册应用程序(Swagger)

  (1)现在,我们将为Swagger添加一个 "Azure AD" 应用程序,并授予它向 "Web API" 应用程序发出请求的权限

      注意重定向URL的地址,这里需要配置 swagger 的回调地址,localhost:9021 是项目运行的地址

    勾选启用隐式授权模式的 ”访问令牌“,”ID令牌“

(2)转到 WebApi 应用添加任意scope(scope名随便定义),那此应用的API将会被公开(暴露),我们这里添加了一个scope(读)

       

  (3)将应用程序ID复制到appsettings中的Swagger:ClientId


(4)转到 “Swagger” 的应用注册点击”添加权限“---》“委托的权限” 来添加下面绿框架中的两个权限,管理员同意后,前端应用就拥有调用后端API的权限了。 

 

 

 

 8,测试效果

  启动项目,在项目的 “Swagger” 首页,点击  Try it out 尝试调用 api/order 接口,Response 提示 401 无访问权限

此时,我们可以在Swagger首页点击 ”Authorize“ ,验证和访问Api资源

 

 

 

登陆Azure账户,进行认证授权

 

 

 

再次调用 api/Order 接口  Response:200 OK

 

 

砰????????????????????????????????,成功!!!!!

 三,结尾

今天的文章大概介绍了如果在我们的项目中集成Azure AD,以及如果在 Swagger中使用隐士授权模式来访问Api资源,

今天,就先分享到这里,上面演示的是如果在Swagger中使用隐式访问模式访问受保护的资源,下一篇继续介绍如何使用其他类型的授权访问模式来访问由Azure AD受保护的API资源。

代码稍等,我会整理一下,上传到github中

作者:Allen 

版权:转载请在文章明显位置注明作者及出处。如发现错误,欢迎批评指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310030.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[C++STL]常用遍历算法

代码如下&#xff1a; #include <iostream> #include <algorithm> #include <vector> using namespace std;void print01(int val) {cout << val << " "; }class print02 { public:void operator()(int val){cout << val <&…

用Visual Studio2019自定义项目模板

项目模板简介众所周知&#xff0c;在我们使用VS新建项目时&#xff0c;都需要选择一个项目模板&#xff0c;如下图&#xff1a;我们选择完项目模板进行创建&#xff0c;创建完成之后&#xff0c;可以发现项目中已经包含了一些基础的文件。例如MVC&#xff1a;可以看到&#xff…

A/B HDU - 1576 (逆元或拓展欧几里得或数学公式)多解法求大数结果

题意&#xff1a;求(A/B)%9973&#xff0c;但由于A很大&#xff0c;我们只给出n(nA%9973)(我们给定的A必能被B整除&#xff0c;且gcd(B,9973) 1)。 思维&#xff1a;&#xff08;1&#xff09;逆元扩展欧几里得算法&#xff1a;满足a*k≡1 (mod p)的k值就是a关于p的乘法逆元。…

[C++STL]常用排序算法

代码如下: #include <iostream> #include <algorithm> #include <vector> using namespace std;void myPrint(int val) {cout << val << " "; }void test01() {vector<int>v;v.push_back(10);v.push_back(30);v.push_back(50);…

新基建火了,开源云计算渠道能做什么?

导语对于开源云计算厂商而言&#xff0c;如果希望在抢滩新基建上构建差异化竞争优势&#xff0c;具备高超的售前技能、售后体验&#xff0c;并拥有创新的技术服务能力与解决方案构建能力是实有必要的。巧了&#xff0c;这些都与渠道构建息息相关。开源云计算厂商在此前的渠道激…

python socket编程之双方相互通信简单实例_Python socket实现的简单通信功能示例

套接字(socket)是计算机网络数据结构&#xff0c;在任何类型的通信开始之前&#xff0c;网络应用程序必须创建套接字&#xff0c;可以将其比作电话的插孔&#xff0c;没有它将无法进行通信常用的地址家族AF_UNIX&#xff1a;基于文件&#xff0c;实现同一主机不同进程之间的通信…

[C++STL]常用拷贝和替换算法

代码如下: #include <iostream> #include <algorithm> #include <vector> #include <ctime> using namespace std;void myPrint(int val) {cout << val << " "; }void test01() {vector<int>v1;for (int i 0; i < 10…

ASP.NET Core分布式项目实战(oauth2 + oidc 实现 client部分)--学习笔记

任务16&#xff1a;oauth2 oidc 实现 client部分实现 client 之前启动一下上一节的 server&#xff0c;启动之前需要清除一些代码注释 Program 的 MigrateDbContextpublic static void Main(string[] args) {BuildWebHost(args)//.MigrateDbContext<ApplicationDbContext&g…

[C++STL]常用算术生成算法

代码如下: #include <iostream> #include <vector> #include <numeric> using namespace std;void test01() {vector<int>v;for (int i 0; i < 10; i){v.push_back(i);}int total accumulate(v.begin(), v.end(), 0);cout << "total …

前端异步对象的原理与使用方法

源宝导读&#xff1a;现今互联网的WEB网站&#xff0c;几乎没有不用到JS异步技术的&#xff0c;虽然大家经常用到&#xff0c;但Javascript提供的异步机制如何才能真正用好呢&#xff0c;可能很多开发小伙伴还有点含糊&#xff0c;本文将从常见的开发场景出发&#xff0c;系统的…

java1.8的stream_JDK1.8新特性(一):stream

搜索热词一.什么是stream&#xff1f;1.概述Java 8 API添加了一个新的抽象称为流Stream&#xff0c;可以让你以一种声明的方式处理数据。这种风格将要处理的元素集合看作一种流&#xff0c; 流在管道中传输&#xff0c; 并且可以在管道的节点上进行处理&#xff0c; 比如筛选&a…

[C++STL]常用集合算法

代码如下: #include <iostream> #include <vector> #include <numeric> #include <algorithm> using namespace std;class myPrint { public:void operator()(int val){cout << val << " ";} };void test01() {vector<int&g…

Seek the Name, Seek the Fame POJ - 2752 (理解KMP函数的失配)既是S的前缀又是S的后缀的子串

题意&#xff1a;给一个字符串S&#xff0c; 求出所有前缀pre&#xff0c;使得这个前缀也正好是S的后缀。 输出所有前缀的结束位置。 就是求前缀和后缀相同的那个子串的长度 然后从小到大输出,主要利用next数组求解。 例如 “ababcababababcabab”&#xff0c; 以下这些前缀…

2020 年了,WPF 还有前途吗?

2020年了&#xff0c;微软的技术也不断更新了, .Net Core 3.1、.Net Framework 4.8以及今年11月推出的.NET 5...win10平台也普及了很多&#xff0c;WPF可以在上面大展身手&#xff0c;可性能和内存占用还是不行&#xff0c;但是WPF强大的UI能力很吸引人。WPF已经凉了吗? 学WPF…

[C++STL]常用查找算法

代码如下: #include <iostream> #include <algorithm> #include <vector> #include <string> using namespace std;void test01() {vector<int>v;for (int i 0; i < 10; i){v.push_back(i);}vector<int>::iterator it find(v.begin(…

php asp 语法,ASP 语法

在我们的 ASP 教程中&#xff0c;每个实例都提供隐藏的 ASP 源代码。这样会使您更容易理解它们的工作原理。向浏览器写输出ASP 文件通常包含 HTML 标签&#xff0c;就像 HTML 文件。然而&#xff0c;ASP 文件也能包含服务器脚本&#xff0c;这些脚本被分隔符 包围起来。服务器脚…

#10003. 「一本通 1.1 例 4」加工生产调度(贪心)

加工生产调度 题目描述 某工厂收到了n个产品的订单&#xff0c;这n个产品分别在A、B两个车间加工&#xff0c;并且必须先在A车间加工后才可以到B车间加工。 某个产品i在A、B两车间加工的时间分别为Ai、Bi。询问怎样安排这n个产品的加工顺序&#xff0c;才能使总的加工时间最短…

不要把异常当做业务逻辑,这性能可能你无法承受

一&#xff1a;背景1. 讲故事在项目中摸爬滚打几年&#xff0c;应该或多或少的见过有人把异常当做业务逻辑处理的情况(┬&#xff3f;┬)&#xff0c;比如说判断一个数字是否为整数,就想当然的用try catch包起来&#xff0c;再进行 int.Parse&#xff0c;如果抛异常就说明不是整…

[设计模式]开闭原则

开闭原则: 对扩展开放&#xff0c;对修改关闭。 增加功能是提过增加代码来实现的&#xff0c;而不是去修改源代码。 代码如下: #include <iostream> #include <string> using namespace std;class Caculaor { public:Caculaor(int a,int b,string c):a(a),b(b…

#10010 「一本通 1.1 练习 6」糖果传递 (数学+贪心)

题目描述 原题来自&#xff1a;HAOI 2008 有 n个小朋友坐成一圈&#xff0c;每人有 ai 颗糖果。每人只能给左右两人传递糖果。每人每次传递一颗糖果的代价为 1 。求使所有人获得均等糖果的最小代价。 输入格式 第一行有一个整数 &#xff0c;n表示小朋友个数&#xff1b; …