1. 程式人生 > >Identity Server4學習系列三 Identity Server4學習系列一 Identity Server4學習系列二之令牌(Token)的概念

Identity Server4學習系列三 Identity Server4學習系列一 Identity Server4學習系列二之令牌(Token)的概念

1、簡介

Identity Server4學習系列一Identity Server4學習系列二之令牌(Token)的概念的基礎上,瞭解了Identity Server4的由來,以及令牌的相關知識,本文開始實戰,實現Identity Server4基本的功能。

 

2、前提

本文基於.Net Core2.1和Indetity Server4 2.3.0,令牌處理包採用IdentityServer4.AccessTokenValidation 2.7.0

 

3、實戰一Identity Server4服務端配置

(1)、專案結構

 

(2)、站點入口檔案Program.cs類

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        /// <summary>
        /// 設定當前專案的伺服器宿主,Windows下預設為IIS
        /// 設定啟動類為Startup類
        /// </summary>
        /// <param name="args"></param>
/// <returns></returns> public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); }

注意:如果時Linux環境,這裡在這裡可以切換站點的宿主伺服器

 

(3)、Startup啟動類(配置Identity Server4的相關引數和MVC的相關引數,並注入到管道模型中)

    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            //優雅的鏈式程式設計
            //注入Identity Server4服務到DI容器中
            services.AddIdentityServer()
           //注入臨時簽名憑據到DI容器,後期可用簽名證書的金鑰替換,用於生成零時金鑰
           .AddDeveloperSigningCredential()
           //注入需要受Identity Server4保護的Api資源添注入到DI容器中 -記憶體級別
           .AddInMemoryApiResources(Apis.GetApiResources())
           //注入需要訪問受Identity Server4保護的Api資源的客戶端注入到DI容器中 -記憶體級別
           .AddInMemoryClients(ThirdClients.GetClients());

            //注入基本的MVC服務
            services.AddMvcCore()
            //注入MVC的認證服務,對應控制器的Authorize特性
           .AddAuthorization()
           //注入MVC格式化程式,對應JsonResult等等的格式化操作,主要用於控制器返回值的格式化操作
           .AddJsonFormatters();

            //注入身份認證服務,設定Bearer為預設方案
            services.AddAuthentication("Bearer")
            //注入並配置Bearer為預設方案的基本引數
            .AddIdentityServerAuthentication(options =>
            {
                //設定令牌的釋出者
                options.Authority = "http://localhost:5000";
                //設定Https
                options.RequireHttpsMetadata = false;
                //需要認證的api資源名稱
                options.ApiName = "api1";
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            //如果當前時開發者模式
            if (env.IsDevelopment())
            {
                //從管道中捕獲同步和非同步System.Exception例項並生成HTML錯誤響應。
                app.UseDeveloperExceptionPage();
            }

            //將IdentityServer 4服務注入到管道模型中(對應上面的IdentityServer 4服務的配置)
            app.UseIdentityServer();

            //將認證服務通過Microsoft.AspNetCore.Authentication.AuthenticationMiddleware中介軟體
            //注入到管道模型中(對應上面認證服務的配置)
            app.UseAuthentication();

            //將mvc新增到Microsoft.AspNetCore.Builder.IApplicationBuilder請求執行中(對應上的MVC配置)
            app.UseMvc();
        }
    }

 

(4)、配置第三方客戶端能成功在認證模式下能成功訪問Api資源的資本引數

    /// <summary>
    /// 配置可以訪問IdentityServer4 保護的Api資源模型的第三方客戶端
    /// 配置客戶端訪問的金鑰
    /// 配置
    /// </summary>
    public class ThirdClients
    {
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>()
            {
                new Client()
                {
                    //客戶端的唯一Id,客戶端需要指定該ClientId才能訪問
                     ClientId = $"client",

                    //no interactive user, use the clientid/secret for authentication
                    //使用客戶端金鑰進行認證
                    AllowedGrantTypes = GrantTypes.ClientCredentials,

                    // 認證金鑰
                    ClientSecrets =
                    {
                        //用Sha256對"secret"進行加密,客戶端必須使用secret金鑰才能成功訪問
                        new Secret("secret".Sha256())
                    },

                    // scopes that client has access to
                    //如果客戶端的金鑰認證成功,限定該金鑰可以訪問的Api範圍
                    AllowedScopes = { "api1" }
                }
            };
        }
    }

注意ClientId(分配給不同客戶端的Id),對應的客戶端呼叫時傳遞過來的ClientId必須一致,否則客戶端發起呼叫時彙報這個錯:

金鑰也是一樣,金鑰是我們分配給客戶端的,客戶端只有給對了我們分配給它的ClientId和金鑰的同時,才能訪問對應的api,所以如果你的金鑰不對,客戶端發起呼叫時也會報這個錯:

 

(5)、配置受保護的Api資源模型

    public class Apis
    {
        //ApiResource -IdentityServer4.Models下的Api資源模型
        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>()
            {
                //Api資源模型
                new ApiResource("api1", "My API")
            };
        }
    }

注意ApiRescourse的名稱必須和Client的AllowedScopes屬性對應,否則客戶端呼叫時會報下面這個錯:

 

(6)、驗證服務端是否配置成功

開啟站點,瀏覽器輸入http://localhost:5000/.well-known/openid-configuration,等到如下返回報文說明服務部署成功:

{
   //令牌簽發者,對應StartUp中的Identity Server4中的認證配置
"issuer":"http://localhost:5000",
//jwt令牌處理地址
"jwks_uri":"http://localhost:5000/.well-known/openid-configuration/jwks", "authorization_endpoint":"http://localhost:5000/connect/authorize", "token_endpoint":"http://localhost:5000/connect/token", "userinfo_endpoint":"http://localhost:5000/connect/userinfo", "end_session_endpoint":"http://localhost:5000/connect/endsession", "check_session_iframe":"http://localhost:5000/connect/checksession", "revocation_endpoint":"http://localhost:5000/connect/revocation", "introspection_endpoint":"http://localhost:5000/connect/introspect", "device_authorization_endpoint":"http://localhost:5000/connect/deviceauthorization", "frontchannel_logout_supported":true, "frontchannel_logout_session_supported":true, "backchannel_logout_supported":true, "backchannel_logout_session_supported":true, "scopes_supported":[ "api1", "offline_access" ], "claims_supported":[ ], "grant_types_supported":[ "authorization_code", "client_credentials", "refresh_token", "implicit", "urn:ietf:params:oauth:grant-type:device_code" ], "response_types_supported":[ "code", "token", "id_token", "id_token token", "code id_token", "code token", "code id_token token" ], "response_modes_supported":[ "form_post", "query", "fragment" ], "token_endpoint_auth_methods_supported":[ "client_secret_basic", "client_secret_post" ], "subject_types_supported":[ "public" ], "id_token_signing_alg_values_supported":[ "RS256" ], "code_challenge_methods_supported":[ "plain", "S256" ] }

引數含義,自行了解

 

3、實戰一客戶端呼叫受Identity Server4保護的Api資源

(1)、前提

客戶端必須安裝IdentityModel 3.10.4包

 

(2)、呼叫程式碼如下:

    class Program
    {
        static void Main(string[] args)
        {
            Request();
            Console.ReadKey();
        }

        async static void Request()
        {
            //請求Identity Server4服務
            var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
            if (disco.IsError)
            {
                Console.WriteLine(disco.Error);
                return;
            }
            //生成Identity Server4授權的客戶端,通過指定對應的ClientId和金鑰(secret)
            var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
            var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

            if (tokenResponse.IsError)
            {
                Console.WriteLine(tokenResponse.Error);
                return;
            }
            Console.WriteLine(tokenResponse.Json);

            //通過Identity Server4的認證過後,拿到AccessToken
            var client = new HttpClient();
            client.SetBearerToken(tokenResponse.AccessToken);
            var response = await client.GetAsync("http://localhost:5000/identity");
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine(response.StatusCode);
            }
            else
            {
                //認證成功,輸出Identity控制器的返回值
                var content = await response.Content.ReadAsStringAsync();
                Console.WriteLine(JArray.Parse(content));
            }
        }
    }

得到如下報文:

同時檢視Identity Server4服務端的輸出:

第一步:客戶端傳入在Indetity Server4中註冊過的分配給該客戶端的ClientId和金鑰,拿到AccessToken

 

第二步:第一次請求目標控制器,並把AcessToken帶過去

第三步:驗證Token是否有效

第四步:Token有效,開始呼叫Identity控制器方法,並拿到響應值

 大致的流程如上.