Azure AD(二)調用受Microsoft 標識平臺保護的 ASP.NET Core Web API 上

一,引言

  上一節講到Azure AD的一些基礎概念,以及Azure AD究竟能夠用來作什麼?本節就接着講如何在咱們的項目中集成Azure AD 保護咱們的API資源(其實這裏還能夠在 SPA單頁面應用,Web項目,移動/桌面應用程序集成Azure AD),好了,廢話很少說,開始今天的內容。html

二,正文

上一篇介紹到 Azure AD 實際上是微軟基於雲的表示和受權訪問管理服務,它能夠幫助咱們在Azure中登陸和訪問資源。咱們能夠經過Azure的標識平臺生成應用程序,採用微軟標識登陸,以及獲取令牌來調用受保護的API資源。也就是說這一切功能也是基於包含Oauth 2.0和Open ID Connect的身份驗證服務。前端

下面先去了解,熟悉一下關於Identity Server 4的 OpenID 和 OAuth 的區別以及受權模式git

若是以前有了解 Identity Server 4 這種受權驗證的框架,能夠跳過下面的介紹:github

identityServer4 知多少(聖傑):http://www.javashuo.com/article/p-kwhgfgpj-ba.htmljson

受權服務器identityServer4 開篇(老張的哲學):http://www.javashuo.com/article/p-onvqrhym-hd.html後端

(一) OpenID 和 OAuth 的區別 (如下的介紹來自google和 OAuth官網)api

  1,OpenID 是一個以用戶爲中心的數字身份識別框架,它具備開放、分散性。OpenID 的建立基於這樣一個概念:咱們能夠經過 URI (又叫 URL 或網站地址)來認證一個網站的惟一身份,簡單通俗的理解,OpenID是用來作爲身份驗證的服務器

  2,OAuth 2.0是用於受權的行業標準協議。OAuth 2.0致力於簡化客戶端開發人員的工做,同時爲Web應用程序,桌面應用程序,移動電話和客廳設備提供特定的受權流程。也就是說 OAuth 2.0 是用來進行受權的
app

  3,OpenID Connect 是基於OAuth 協議的簡單身份層。它容許客戶端基於受權服務器執行的身份驗證來驗證最終用戶的身份,並以可互操做且相似於REST的方式獲取有關最終​​用戶的基本配置文件信息。OpenID Connect容許全部類型的客戶端(包括基於Web的客戶端,移動客戶端和JavaScript客戶端)請求並接收有關通過身份驗證的會話和最終用戶的信息。規範套件是可擴展的,容許參與者在對他們有意義的時候使用可選功能,例如身份數據加密,OpenID提供程序的發現以及會話管理。框架

  OpenID Connect執行許多與OpenID 2.0相同的任務,可是這樣作的方式是API友好的,而且可由本機和移動應用程序使用,OpenID Connect定義了用於可靠簽名和加密的可選機制。OAuth 1.0和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上(文章底部的github連接)

2,安裝: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 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

            // open authentication middleware
            app.UseAuthentication();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
startup.cs
{
  "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
  }
}
application.json
    [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)
        {
        }
    }
OrderController.cs

7,項目添加Swagger的配置,使用Swagger進行接口測試-

  7.1:安裝  Swashbuckle.AspNetCore,Swashbuckle.AspNetCore.Filters

  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中

github:https://github.com/allentmater/Azure.AD.WebApi.git

做者:Allen 

版權:轉載請在文章明顯位置註明做者及出處。如發現錯誤,歡迎批評指正。

相關文章
相關標籤/搜索