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: empis4IdentityServer> 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(); //}); } } }