1. 程式人生 > >.NET Core微服務之基於IdentityServer建立授權與驗證服務(續)

.NET Core微服務之基於IdentityServer建立授權與驗證服務(續)

上一篇我們基於IdentityServer4建立了一個AuthorizationServer,並且繼承了QuickStartUI,能夠成功獲取Token了。這一篇我們瞭解下如何整合API Service和MVC Web Application。

一、整合API Service

1.1 新增ASP.NET Core WebAPI專案

  新建兩個WebAPI程式,假設這裡取名為ApiService01(佔用埠5010)和ApiService02(佔用埠5020)。

  

  為了方便快速啟動,可以繼續在launchSettings.json中刪掉關於IISExpress的部分,由於是WebAPI,所以也不需要啟動瀏覽器(將其設為false):

{
  "profiles": {
    "Manulife.DNC.MSAD.IdentityServer4Test.ApiService01": {
      "commandName": "Project",
      "launchBrowser": false,
      "launchUrl": "api/values",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "http://localhost:5010/
" } } }

1.2 安裝IdentityServer4.AccessTokenValidation

NuGet>Install-Package IdentityServer4.AccessTokenValidation  

  安裝完成之後,需要做一些適配,所以我們來註冊一下關於其的配置:這裡設定的預設模式是Bearer,其中AddIdentityServerAuthentication方法是將我們上一篇建立的授權伺服器註冊為token的處理人,即在本API程式中涉及到token的處理,都會移交給指定伺服器(這裡即上一篇中建立的AuthorizationServer)進行處理。

    public void ConfigureServices(IServiceCollection services)
    {
        ......

        // IdentityServer
        services.AddMvcCore().AddAuthorization().AddJsonFormatters();
        services.AddAuthentication(Configuration["Identity:Scheme"])
            .AddIdentityServerAuthentication(options =>
            {
                options.RequireHttpsMetadata = false; // for dev env
                options.Authority = $"http://{Configuration["Identity:IP"]}:{Configuration["Identity:Port"]}";
                options.ApiName = Configuration["Service:Name"]; // match with configuration in IdentityServer
            });

        // Swagger
        ......
    }

  配置檔案中的內容如下所示:這裡需要注意的是Name需要和我們在上一篇中的InMemoryConfiguration中自行hard-code的一樣

{
  "Service": {
    "Name": "clientservice",
    "Port": "5010",
    "DocName": "clientservice",
    "Version": "v1",
    "Title": "CAS Client Service API",
    "Description": "CAS Client Service API provide some API to help you get client information from CAS",
    "Contact": {
      "Name": "CAS 2.0 Team",
      "Email": "[email protected]"
    },
    "XmlFile": "Manulife.DNC.MSAD.IdentityServer4Test.ApiService01.xml"
  },
  "Identity": {
    "IP": "localhost",
    "Port": "5000",
    "Scheme": "Bearer"
  }
}
View Code

  此外,還需要在Configure方法中配置Authentication中介軟體:這裡需要注意的就是需要放在UseMvc()之前呼叫

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        // authentication
        app.UseAuthentication();

        app.UseMvc();

        // swagger
        .......
    }

  以上是ApiService01的配置,ApiService02的配置類似,只是配置檔案中的資訊從clientservice改為了productservice。

1.3 為要進行驗證授權的方法新增[Authorize]特性

  由於我們建立WebAPI時,預設有一個ValuesController,保留它,我們直接為這個Controller新增一個[Authorize]特性。

    [Authorize]
    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        ......
    }

  這樣的話,剛剛註冊的中介軟體就會在請求的過程中基於傳遞過來的token進行Authorization,如果沒有token或者token是非法的,它就會告訴api的消費者這個請求時未授權的(HTTP StatusCode 401)

1.4 簡單測試一下

  測試之前首先確保AuthorizationServer和兩個ApiService都一起啟動,可以在解決方案屬性的啟動選項中進行設定。

  (1)不帶token的情況

  

  (2)帶正確token的情況

  首先請求獲取一下token:這裡我們使用的grant_type是client_credentials,也可以使用password(需要輸入使用者名稱和密碼)。

  

  帶上這個token再去呼叫api service

  

  (3)帶不正確的token的情況(這裡簡單改一下token的值)

  

  (4)用剛剛授予(clientservice)的token訪問未經授權的productservice

  

  我們也可以通過在ValuesController中加上如下的一句,來獲取Claims => 它是從已驗證過的token中抽取的資訊。如果我們將上面獲取的token的grant_type設定為password的話,那我們會在Claims中獲取到使用者ID的資訊。

  

  

二、整合MVC Web Application

2.1 新增ASP.NET Core MVC專案

  新建一個ASP.NET Core MVC專案,這裡假設取名為MvcClient(佔用埠5100)

  仍然修改launchSettings.json,刪掉IISExpress部分,但仍然選擇啟動瀏覽器:

{
  "profiles": {
    "Manulife.DNC.MSAD.IdentityServer4Test.MvcClient": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "http://localhost:5100/"
    }
  }
}

2.2 為指定方法新增[Authorize]特性

  這裡我們在HomeController的About方法上面新增[Authorize]特性:

    [Authorize]
    public IActionResult About()
    {
        ViewData["Message"] = "Your application description page.";

        return View();
    }

  這時如果我們直接訪問About,會拋異常,告知我們No authenticationScheme was specified......

  而我們想要的效果是當User第一次點選About,頁面重定向到AuthorizationServer (localhost:5000),當用戶登入驗證授權之後,再重定向到該網站。此後一定時間範圍內的第二次,第三次點選,都不再需要再重定向到AuthorizationServer,而是直接讀取儲存的token。

2.3 新增OpenID Connect Authentication

  這一部分主要集中於做Authentication(身份驗證)而非Authorization(授權)。

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); // ensure not change any return Claims from Authorization Server
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc"; // oidc => open ID connect
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = "Cookies";
            options.Authority = $"http://{Configuration["Identity:IP"]}:{Configuration["Identity:Port"]}";
            options.RequireHttpsMetadata = false; // please use https in production env
            options.ClientId = "cas.mvc.client.implicit";
            options.ResponseType = "id_token token"; // allow to return access token
            options.SaveTokens = true;
        });
    }

  這裡我們使用的是implicit這個flow(詳細內容可以閱讀ddrsql的IdentityServer4之Implicit(隱式許可)),它主要用於客戶端應用程式(主要指基於javascript的應用),它允許客戶端程式重定向到AuthorizationServer,然後帶著token重定向回來。值得一提的是這裡的ResponseType為"id_token token",表示既獲取id_token也獲取access_token。而SaveTokens設為true則表示要將從AuthorizationServer返回的token持久化在cookie之中,這樣就不用每次都去請求token了。

  當然,也還得在Configure方法中,配置Authentication中介軟體:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseBrowserLink();
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseAuthentication();

        app.UseStaticFiles();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

  *.位置仍然需要在UseMvc之前。

2.4 在AuthorizationServer新增這個MvcClient

   在InMemoryConfiguration類中修改GetClients方法:

    public static IEnumerable<Client> GetClients()
    {
        return new[]
        {
            new Client
            {
                ClientId = "client.api.service",
                ClientSecrets = new [] { new Secret("clientsecret".Sha256()) },
                AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                AllowedScopes = new [] { "clientservice" }
            },
            ......,
            new Client
            {
                ClientId = "cas.mvc.client.implicit",
                ClientName = "CAS MVC Web App Client",
                AllowedGrantTypes = GrantTypes.Implicit,
                RedirectUris = { $"http://{Configuration["Clients:MvcClient:IP"]}:{Configuration["Clients:MvcClient:Port"]}/signin-oidc" },
                PostLogoutRedirectUris = { $"http://{Configuration["Clients:MvcClient:IP"]}:{Configuration["Clients:MvcClient:Port"]}/signout-callback-oidc" },
                AllowedScopes = new [] {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "agentservice", "clientservice", "productservice"
                },
                AllowAccessTokensViaBrowser = true // can return access_token to this client
            }
        };
    }

  這裡的ClientId要和MvcClient中設定的一致。RedirectUris是指登入成功之後需要重定向的地址(這裡這個位置在MvcClient中),而PostLogoutRedirectUris是指登出之後需要重定向的地址。和API Service Client的設定不同的就是在AllowedScopes中給它增加了OpenId和Profile,因為我們為MvcClient設定的是oidc而不是bearer的模式。最後為了使用這些OpenID Connect Scopes,需要設定這些Identity Resources:

    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
        };
    }

    public void ConfigureServices(IServiceCollection services)
    {
        .......

        services.AddIdentityServer()
            //.AddDeveloperSigningCredential()
            .AddSigningCredential(new X509Certificate2(Path.Combine(basePath,
                Configuration["Certificates:CerPath"]),
                Configuration["Certificates:Password"]))
            .AddInMemoryIdentityResources(InMemoryConfiguration.GetIdentityResources())
            .AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
            .AddInMemoryClients(InMemoryConfiguration.GetClients())
            .AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());
        ......
    }

  同時,為了演示方便,我們在MvcClient的About檢視中新增幾句:

@{
    ViewData["Title"] = "About";
}
<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"]</h3>

@using Microsoft.AspNetCore.Authentication
<div>
    <strong>id_token</strong>
    <span>@await ViewContext.HttpContext.GetTokenAsync("id_token")</span>
</div>
<div>
    <strong>access_token</strong>
    <span>@await ViewContext.HttpContext.GetTokenAsync("access_token")</span>
</div>

<dl>
    @foreach (var claim in User.Claims)
    {
        <dt>@claim.Type</dt>
        <dd>@claim.Value</dd>
    }
</dl>

  這裡我們將id_token, access_token(只有拿到access_token,才可以在MvcClient中發起請求呼叫API Service,而具體可以訪問哪些API Service是在InMemoryConfiguration中配置的AllowScopes裡面的那些)都顯示出來,還會將Claims資料也顯示出來(這裡的Claims資料就是從AuthorizationServer返回的token裡面payload部分的資料,關於payload請搜尋JWT)

2.5 簡單測試一下

  (1)未登陸的情況

  

  拿到access_token,可以去JWT.io上進行解析看看:比如關注payload部分,可以得到很多資訊,比如過期時間為1小時(預設)

  

  (2)已登入的情況

  這裡為了方便演示,新增一個Logout方法:這裡需要確保同時登出MvcClient的Cookies和OpenId Connect(即到Identity Server去清除單點登入的Session)。

    public class HomeController: Controller
    {
        ......

        public async Task Logout()
        {
            await HttpContext.SignOutAsync("Cookies");
            await HttpContext.SignOutAsync("oidc");
        }

        ......
    }

  

  最後,關於access token的生命週期,可以閱讀一下園友曉晨Master(李志強)的《IdentityServer4實戰 - AccessToken 生命週期分析》,裡面提到一個時間偏移的概念,需要了解一下。另外,如果是前後端分離的結構,也可以瀏覽一下ddrsql的《IdentityServer4之Implicit(隱式許可) —— oidc-client-js前後端分離》,裡面介紹了oidc-client這個JS庫的使用,以及如何支援跨域。

三、小結

  本篇基於上一篇搭建好的AuthorizationServer,通過整合API Service與MVC Web Application來演示他們如何與Authorization Server的互動,瞭解了兩種不同的Scheme(Bearer和Implicit),最後補充了一些材料供擴充套件閱讀。但是,IdentityServer的內容很多,我只是學習了一些我要掌握以做POC的部分,其他還有很多功能和知識點我沒有學習,大家可以通過搜尋園子裡各種大神(eg.曉晨Master, solenovex等等等等)的文章進行學習。後面我會將IdentityServer與Ocelot進行整合,嘗試在API閘道器處做統一驗證與授權。最後,感謝參考資料的作者們,本篇主要基於參考資料的學習而成的筆記。另外,一些朋友十分想要一些基於生產環境的採坑經驗以及更加實際的應用案例,對此我只能說聲抱歉,我目前仍然處於學習與準備POC階段,目的是為了在公司裡推廣以及給老闆安利(雖然很難),還沒有實際專案遷移到.NET Core微服務架構上去,但是如果不努力讓公司遷移和應用,那像我司一樣的傳統行業的金融保險公司是不會主動升級的,還請各位諒解。

  

示例程式碼

參考資料

作者:周旭龍

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。

相關推薦

.NET Core服務基於IdentityServer建立授權驗證服務

上一篇我們基於IdentityServer4建立了一個AuthorizationServer,並且繼承了QuickStartUI,能夠成功獲取Token了。這一篇我們瞭解下如何整合API Service和MVC Web Application。 一、整合API Service 1.1 新增ASP.NE

.NET Core服務基於IdentityServer建立授權驗證服務

一、IdentityServer的預備知識   要學習IdentityServer,事先得了解一下基於Token的驗證體系,這是一個龐大的主題,涉及到Token,OAuth&OpenID,JWT,協議規範等等等等,園子裡已經有很多介紹的文章了,個人覺得solenovex的這一篇文章《學習Id

.Net Core 認證系統基於Identity Server4 Token的JwtToken認證原始碼解析

介紹JwtToken認證之前,必須要掌握.Net Core認證系統的核心原理,如果你還不瞭解,請參考.Net Core 認證元件原始碼解析,且必須對jwt有基本的瞭解,如果不知道,請百度.最重要的是你還需要掌握identity server4的基本用法,關於identity server4因為設計到兩個協議O

從壹開始服務 [ DDD ] 五 ║聚合:實體值物件

前言 哈嘍,老張是週四放鬆又開始了,這些天的工作真的是繁重,三個專案同時啟動,沒辦法,只能在深夜寫文章了,現在時間的週四凌晨,白天上班已經沒有時間開始寫文章了,希望看到文章的小夥伴,能給個辛苦贊

.net core webapi +ddd(領域驅動)+nlog配置+swagger配置 學習筆記2

struct 名詞 領域驅動設計 .com 模型 webapi 添加 傳統 bapi DDD領域驅動模型設計 什麽是DDD 軟件開發不是一蹴而就的事情,我們不可能在不了解產品(或行業領域)的前提下進行軟件開發,在開發前,通常需要進行大量的業務知識梳理,而後到達軟件設計的

循序漸進學.Net Core Web Api開發系列【13】:中介軟體Middleware

系列目錄 一、概述 本篇介紹如何使用中介軟體(Middleware)。 二、初步演練 先寫幾個中介軟體 public class DemoAMiddleware { private readonly RequestDelegate _next;

粒子群優化演算法(PSO)基於離散化的特徵選擇(FS)

作者:Geppetto 前面我們介紹了特徵選擇(Feature Selection,FS)與離散化資料的重要性,總覽的介紹了PSO在FS中的重要性和一些常用的方法。今天講一講FS與離散化的背景,介紹本文所採用的基於熵的切割點和最小描述長度原則(MDLP

Asp.net Core 使用Jenkins + Dockor 實現持續集成、自動化部署:發布回滾

截圖 begin 更強 else https 分發 集成測試 block 執行 寫在前面 我們以前windows跑.net Framework程序的時候,發布,自己乖乖的替換程序;備份,也是自己一個一個的重命名備份;回滾,發布遇到問題的回滾更是不用說了;運維很是怕我們 這

.NET Core開發的iNeuOS物聯網平臺部署樹黴派raspbian,從閘道器到雲端整體解決方案。助力2019中國.NET峰會。

2019 中國.NET 開發者峰會正式啟動 目       錄 1.      概述... 2 2.      樹莓派硬體配置... 2 3.

ABP (.Net Core 3.1版本) 使用MySQL資料庫遷移啟動模板專案1

> 最近要搭建新專案,因為還沒有用過.net core,所以想用.net core的環境搭建新專案,因為不熟悉.net core的架構,所以就下載了abp專案先了解一下。 因為自己太菜了,下載了模板專案,在啟動的過程中一波三折,其曲折真是無法用言語形容。(但是我沒有灰心!沒有什麼技術是在努力的情況下學

信小程式視訊視訊背景控制處理筆記

想用一個全屏的視訊當做小程式背景,然後呢,更坑爹啊,上傳單個素材2M限制。 注意: 1:用video元件時候,原生元件z-index無論如何都不會比他更高的,要用新的cover-view 2:視訊格式MP4支援AVC(H264)把你的視訊拖到chrome試試,就知道了。

基於zookeeper實現分布式鎖

src 9.png 分享 per 分享圖片 技術分享 zookeeper 圖片 http 測試代碼: 效果圖: 基於zookeeper實現分布式鎖(續)

死磕 java執行緒系列自己動手寫一個執行緒池

(手機橫屏看原始碼更方便) 問題 (1)自己動手寫的執行緒池如何支援帶返回值的任務呢? (2)如果任務執行的過程中丟擲異常了該

.NET Core服務基於Ocelot+IdentityServer實現統一驗證授權

一、案例結構總覽  這裡,假設我們有兩個客戶端(一個Web網站,一個移動App),他們要使用系統

.NET Core服務基於Consul實現服務治理

請求轉發 1.0 asp.net AC port prefix 我們 tle nan 一、Consul基礎介紹   Consul是HashiCorp公司推出的開源工具,用於實現分布式系統的服務發現與配置。與其他分布式服務註冊與發現的方案,比如 Airbnb的Smart

.NET Core服務基於Consul實現服務治理

shell pla code tst 分層 編輯 set req \n 上一篇發布之後,這一篇把上一篇沒有弄到的東西補一下,也算是給各位前來詢問的朋友的一些回復吧。一、Consul服務註冊之配置文件方式1.1 重溫Consul實驗集群  這裏我們有三個Consul Serv

基於Apollo實現.NET Core服務統一配置(測試環境-單機) .NET Core服務基於Apollo實現統一配置中心

一、前言 注:此篇只是為測試環境下的快速入門。後續會給大家帶來生產環境下得實戰開發。 具體的大家可以去看官方推薦。非常的簡單明瞭。以下介紹引用官方內容: Apollo(阿波羅)是攜程框架部門研發的分散式配置中心,能夠集中化管理應用不同環境、不同叢集的配置,配置修改後能夠實時推送到應用端,並且具

.NET Core服務基於Steeltoe使用Eureka實現服務註冊發現

一、關於Steeltoe與Spring Cloud    Steeltoe is an open source project that enables .NET developers to implement industry standard best practices when b

.NET Core服務基於Steeltoe整合Zuul實現統一API閘道器

一、關於Spring Cloud Zuul   API Gateway(API GW / API 閘道器),顧名思義,是出現在系統邊界上的一個面向API的、序列集中式的強管控服務,這裡的邊界是企業IT系統的邊界。   Zuul 是Netflix 提供的一個開源元件,致力於在雲平臺上提供動態路由,監

.NET Core服務基於Steeltoe使用Hystrix熔斷保護監控

一、關於Spring Cloud Hystrix      在微服務架構中,我們將系統拆分為很多個服務,各個服務之間通過註冊與訂閱的方式相互依賴,由於各個服務都是在各自的程序中執行,就有可能由於網路原因或者服務自身的問題導致呼叫故障或延遲,隨著服務的積壓,可能會導致服務崩潰。為了解決這一系列的問題