IdentityServer4 是一個基於 .NET Core 的 OpenID Connect 實現框架。
基於框架創建可運行的應用,通常還需要多個步驟,添加引用、配置項目、框架初始化、按照一系列步驟啟動應用等等。那么,基於 IdentityServer4 創建一個可運行的 OpenID Connect 服務器需要多少行代碼呢?
得益於 .net core 提供的項目模版支持,實際上,不需要你寫一行代碼,只需要執行幾個簡單的命令就可以了。
1. 准備 IdentityServer4 模版
還記得使用 .net core 的命令行工具 dotnet 創建項目使用的 new 命令嗎?如果你希望創建名為 HelloWorld 的項目,那么,創建它的命令如下:
dotnet new console -n HelloWorld
該命令會在當前目錄下創建 HelloWorld 子目錄。命令執行完成之后,進入這個 HelloWorld 目錄中,就可以通過 run 命令來運行了。
>cd HelloWorld >dotnet run Hello World!
這個 console 就是通過內置的模版來實現的。當前支持的模版可以通過 list 子命令列出
dotnet new -list
在 dotnet 中,還可以通過 -i 參數來安裝新的模版。模版既可以通過一個路徑提供,也可以通過一個 nuget_id 來提供。你可能已經想到了,IdentityServer4 就已經提供了一個預定義在 NuGet 中的模版:IdentityServer4.Templates,你可以直接在 NuGet 中找到它:https://www.nuget.org/packages/IdentityServer4.Templates。需要說明的是,同一個項目模版可能存在多個版本,在安裝的時候,可以通過在模版名稱后面使用兩個冒號來分隔特定的版本號,默認情況下,只安裝最新的穩定版本。
所以,事情變得簡單了。我們需要的就是先在本地安裝這個模版,然后,使用這個模版就可以直接創建出完整的 IdentityServer4 項目,然后直接運行。
dotnet new --install IdentityServer4.Templates
NuGet 中的提示是安裝特定的版本,現在的最新穩定版本是 3.1.1,所以命令變成了
dotnet new --install IdentityServer4.Templates::3.1.1
在命令的輸出中,可以看到已經安裝了多個關於 IdentityServer4 的模版
Templates | Short Name | Language | Tags |
---|---|---|---|
IdentityServer4 with AdminUI | is4admin | [C#] | Web/IdentityServer4 |
IdentityServer4 with ASP.NET Core Identity | is4aspid | [C#] | Web/IdentityServer4 |
IdentityServer4 Empty | is4empty | [C#] | Web/IdentityServer4 |
IdentityServer4 with Entity Framework Stores | is4ef | [C#] | Web/IdentityServer4 |
IdentityServer4 with In-Memory Stores and Test Users | is4inmem | [C#] | Web/IdentityServer4 |
IdentityServer4 Quickstart UI (UI assets only) | is4ui | [C#] | Web/IdentityServer4 |
2. 創建第一個 IdentityServer4 項目
直接使用 .net core 的 new 命令來創建項目,我們將創建的項目命名為 IdentityServer
dotnet new is4empty -n IdentityServer
執行之后,會在當前目錄下創建 IdentityServer 文件夾,所有的項目文件都位於其中。
3. 啟動應用
使用 .net core 的 run 命令來啟動項目,可以看到如下輸出:
dotnet new is4empty -n IdentityServer PS C:\temp\is4\IdentityServer> dotnet run [19:34:49 Information] Starting host... [19:34:50 Information] IdentityServer4.Startup Starting IdentityServer4 version 3.1.0.0 [19:34:50 Information] IdentityServer4.Startup You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refrtion. [19:34:50 Information] IdentityServer4.Startup Using the default authentication scheme idsrv for IdentityServer [19:34:50 Debug] IdentityServer4.Startup Using idsrv as default ASP.NET Core scheme for authentication [19:34:50 Debug] IdentityServer4.Startup Using idsrv as default ASP.NET Core scheme for sign-in [19:34:50 Debug] IdentityServer4.Startup Using idsrv as default ASP.NET Core scheme for sign-out [19:34:50 Debug] IdentityServer4.Startup Using idsrv as default ASP.NET Core scheme for challenge [19:34:50 Debug] IdentityServer4.Startup Using idsrv as default ASP.NET Core scheme for forbid
祝賀你!你已經成功創建了第一個可運行的 IdentityServer4 服務器!
4. 訪問配置信息
雖然還沒有寫一行代碼,但是,服務器已經在工作了,我們可以通過它的 .well-known 端點來訪問服務器的配置信息,在瀏覽器的地址欄中,輸入地址:http://localhost:5000/.well-known/openid-configuration,並回車。應該可以看到如下的響應信息。
{ "issuer": "http://localhost:5000", "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": [ "openid", "offline_access" ], "claims_supported": [ "sub" ], "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" ], "id_token_signing_alg_values_supported": [ "RS256" ], "subject_types_supported": [ "public" ], "code_challenge_methods_supported": [ "plain", "S256" ], "request_parameter_supported": true }
5. 查看項目代碼
5.1 項目文件
項目文件名稱為 IdentityServer4.csproj,內容如下:
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="IdentityServer4" Version="3.1.0" /> <PackageReference Include="Serilog.AspNetCore" Version="3.2.0" /> <!-- <PackageReference Include="System.Security.Principal.Windows" Version="4.7.0" /> --> </ItemGroup> </Project>
主要做了 3 件事:
-
配置了項目為 Web 應用項目,通過 Sdk 屬性指定了
Microsoft.NET.Sdk.Web
,目標框架為 .net core 3.1。 -
添加了對 IdentityServer4 的引用,這就是 IdentityServer4 的實現庫
- 添加了日志庫
Serilog.AspNetCore
5.2 Program.cs
這里面相當多的代碼是關於 Serilog
日志的配置,關於 Web 站點,只是使用了標准的 Startup
方式啟動。
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using Serilog; using Serilog.Events; using Serilog.Sinks.SystemConsole.Themes; using System; namespace IdentityServer { public class Program { public static int Main(string[] args) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) .Enrich.FromLogContext() .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate) .CreateLogger(); try { Log.Information("Starting host..."); CreateHostBuilder(args).Build().Run(); return 0; } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly."); return 1; } finally { Log.CloseAndFlush(); } } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); webBuilder.UseSerilog(); }); } }
5.3 Startup.cs
在 ConfigureServices()
中,最為關鍵的一行就是添加了 IdentityServer4
服務的支持。
由於這是用於演示的空項目,所以,具體的 OpenID Connect 配置信息來自於一個代碼文件 Config
,並使用硬編碼的方式來用於 IdentityServer
var builder = services.AddIdentityServer() .AddInMemoryIdentityResources(Config.Ids) .AddInMemoryApiResources(Config.Apis) .AddInMemoryClients(Config.Clients);
這個 Config.cs
文件中,還沒有對這些信息進行配置。
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. using IdentityServer4.Models; using System.Collections.Generic; namespace IdentityServer { public static class Config { public static IEnumerable<IdentityResource> Ids => new IdentityResource[] { new IdentityResources.OpenId() }; public static IEnumerable<ApiResource> Apis => new ApiResource[] { }; public static IEnumerable<Client> Clients => new Client[] { }; } }
在 Configure()
中,則啟用了 IdentityServer
服務。
app.UseIdentityServer();
完整的代碼如下所示:
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace IdentityServer { public class Startup { public IWebHostEnvironment Environment { get; } public Startup(IWebHostEnvironment environment) { Environment = environment; } public void ConfigureServices(IServiceCollection services) { // uncomment, if you want to add an MVC-based UI //services.AddControllersWithViews(); var builder = services.AddIdentityServer() .AddInMemoryIdentityResources(Config.Ids) .AddInMemoryApiResources(Config.Apis) .AddInMemoryClients(Config.Clients); // not recommended for production - you need to store your key material somewhere secure builder.AddDeveloperSigningCredential(); } public void Configure(IApplicationBuilder app) { if (Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // uncomment if you want to add MVC //app.UseStaticFiles(); //app.UseRouting(); app.UseIdentityServer(); // uncomment, if you want to add MVC //app.UseAuthorization(); //app.UseEndpoints(endpoints => //{ // endpoints.MapDefaultControllerRoute(); //}); } } }
參考資料