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控制器方法,並拿到響應值
大致的流程如上.