IdentityServer4中文文档星火卓越

IdentityServer4是ASP.NETCore2的OpenIDConnect和OAuth2.0框架。

它在您的应用程序中启用以下功能:

为各种类型的客户端发出API访问令牌,例如服务器到服务器,Web应用程序,SPA和本机/移动应用程序。

支持AzureActiveDirectory,Google,Facebook等外部身份提供商。这可以保护您的应用程序免受如何连接到这些外部提供商的详细信息的影响。

最重要的部分-IdentityServer的许多方面都可以根据您的需求进行定制。由于IdentityServer是一个框架而不是盒装产品或SaaS,因此您可以编写代码以使系统适应您的方案。

大多数现代应用程序或多或少看起来像这样:

最常见的互动是:

将这些基本安全功能外包给安全令牌服务可防止在这些应用程序和端点之间复制该功能。

重构应用程序以支持安全令牌服务会产生以下体系结构和协议:

这种设计将安全问题分为两部分:

当应用程序需要知道当前用户的身份时,需要进行身份验证。通常,这些应用程序代表该用户管理数据,并且需要确保该用户只能访问允许的数据。最常见的例子是(经典)Web应用程序-但是基于本机和JS的应用程序也需要身份验证。

最常见的身份验证协议是SAML2p,WS-Federation和OpenIDConnect-SAML2p是最受欢迎和最广泛部署的。

OpenIDConnect是三者中的最新产品,但被认为是未来,因为它具有最大的现代应用潜力。它是从一开始就为移动应用场景而构建的,旨在实现API友好。

应用程序有两种与API通信的基本方式-使用应用程序标识或委派用户的标识。有时两种方法都需要结合起来。

OpenIDConnect和OAuth2.0非常相似-事实上,OpenIDConnect是OAuth2.0之上的扩展。两个基本的安全问题,即身份验证和API访问,被合并为一个协议-通常只需要一次往返安全令牌服务。

我们相信,OpenIDConnect和OAuth2.0的结合是在可预见的未来保护现代应用程序的最佳方法。IdentityServer4是这两种协议的实现,经过高度优化,可以解决当今移动,本机和Web应用程序的典型安全问题。

IdentityServer是一个中间件,可将符合规范的OpenIDConnect和OAuth2.0端点添加到任意ASP.NETCore应用程序中。

规范,文档和对象模型使用您应该注意的某些术语。

IdentityServer是OpenIDConnect提供程序-它实现OpenIDConnect和OAuth2.0协议。

但它们完全相同:一种向客户发放安全令牌的软件。

IdentityServer具有许多作业和功能-包括:

用户是使用注册客户端访问资源的人。

客户端是从IdentityServer请求令牌的软件-用于验证用户(请求身份令牌)或访问资源(请求访问令牌)。客户端必须首先向IdentityServer注册,然后才能请求令牌。

客户端的示例包括Web应用程序,本机移动或桌面应用程序,SPA,服务器进程等。

您希望使用IdentityServer保护资源-用户的身份数据或API。

每个资源都有一个唯一的名称-客户端使用此名称来指定他们希望访问哪些资源。

APIAPI资源表示客户端要调用的功能-通常建模为WebAPI,但不一定。

IdentityServer实现以下规范:

IdentityServer由许多nuget包组成。

包含核心IdentityServer对象模型,服务和中间件。仅包含对内存配置和用户存储的支持-但您可以通过配置插入对其他存储的支持。这是其他回购和包装的内容。

用于验证API中令牌的ASP.NETCore身份验证处理程序。处理程序允许在同一API中支持JWT和引用令牌。

IdentityServer的ASP.NET核心身份集成包。该软件包提供了一个简单的配置API,可以为IdentityServer用户使用ASP.NET身份管理库。

EntityFrameworkIdentityServer的核心存储实现。此程序包为IdentityServer中的配置和操作存储提供EntityFramework实现。

此外,我们将开发/临时构建发布到MyGet。如果要尝试尝试,请将以下Feed添加到VisualStudio:

我们为IdentityServer提供了多种免费和商业支持和咨询选项。

免费支持是基于社区的,并使用公共论坛

堆栈溢出

IdentityServer4在提出新问题时请使用标签

小胶质

您可以在我们的Gitter聊天室中与其他IdentityServer4用户聊天:

报告错误

训练

AdminUI,IdentityExpress和SAML2p支持

我们对社区贡献非常开放,但您应该遵循一些指导方针,以便我们可以毫不费力地处理这个问题。

最简单的贡献方式是打开一个问题并开始讨论。然后我们可以决定是否以及如何实现功能或更改。如果您应该提交带有代码更改的pull请求,请从描述开始,只进行最小的更改并提供涵盖这些更改的测试。

IdentityServer是针对ASP.NETCore2构建的,可在.NETFramework4.6.1(及更高版本)和.NETCore2(及更高版本)上运行。

请在相应的GitHub仓库中记录一个新问题:

在您提供任何代码或内容之前,您需要签署贡献者许可协议。这是一个自动过程,将在您打开拉取请求后启动。

注意

我们只接受开发分支的PR。

如果您启动贡献项目(例如,支持DatabaseX或ConfigurationStoreY),我们非常感谢。告诉我们,我们可以在我们的文档中发推文和链接。

我们通常不想拥有这些贡献库,我们已经非常忙于支持核心项目。

命名约定

截至2017年10月,IdentityServer4。*nuget名称空间保留给我们的软件包。请使用以下命名约定:

YourProjectName.IdentityServer4

要么

IdentityServer4.Contrib.YourProjectName

启动新IdentityServer项目有两种基本方法:

如果您从头开始,我们提供了几个帮助程序和内存存储,因此您不必担心从一开始就存在持久性。

如果您从ASP.NET身份开始,我们也提供了一种简单的方法来集成它。

快速入门提供了各种常见IdentityServer方案的分步说明。他们从绝对的基础开始,变得更加复杂-建议你按顺序完成它们。

屏幕截图显示了VisualStudio-但这不是必需的。

创建快速入门IdentityServer

首先创建一个新的ASP.NETCore项目。

然后选择“清空”选项。

接下来,添加IdentityServer4nuget包:

或者,您可以使用程序包管理器控制台通过运行以下命令来添加依赖项:

“安装包IdentityServer4”

IdentityServer构建编号1.x目标ASP.NETCore1.1,IdentityServer构建编号2.x目标ASP.NETCore2.0。

IdentityServer使用通常的模式为ASP.NETCore主机配置和添加服务。在ConfigureServices所需的服务中配置并添加到DI系统。在Configure中间件中添加到HTTP管道。

将Startup.cs文件修改为如下所示:

IdentityServer尚未准备好启动。我们将在以下快速入门中添加所需的服务。

默认情况下,VisualStudio使用IISExpress来托管您的Web项目。这完全没问题,除了您将无法看到控制台的实时日志输出。

IdentityServer广泛使用日志记录,而UI中的“可见”错误消息或返回给客户端是故意模糊的。

我们建议在控制台主机中运行IdentityServer。您可以通过在VisualStudio中切换启动配置文件来完成此操作。每次启动IdentityServer时也不需要启动浏览器-您也可以关闭它:

我们建议为IISExpress和自托管配置相同的端口。这样,您可以在两者之间切换,而无需修改客户端中的任何配置。

要在启动时选择控制台主机,必须在VisualStudio的启动菜单中选择它:

运行快速入门解决方案各个部分的最简单方法是将启动模式设置为“当前选择”。右键单击解决方案并选择“设置启动项目”:

通常,首先启动IdentityServer,然后启动API,然后启动客户端。如果您确实想要调试,只能在调试器中运行。否则Ctrl+F5是运行项目的最佳方式。

本快速入门介绍了使用IdentityServer保护API的最基本方案。

在这种情况下,我们将定义一个API和一个想要访问它的客户端。客户端将在IdentityServer请求访问令牌并使用它来获取对API的访问权限。

范围定义了您要保护的系统中的资源,例如API。

由于我们在本演练中使用内存配置-您只需创建一个类型的对象ApiResource并设置适当的属性即可。

将文件(例如Config.cs)添加到项目中并添加以下代码:

publicstaticIEnumerableGetApiResources(){returnnewList{newApiResource("api1","MyAPI")};}定义客户端下一步是定义可以访问此API的客户端。

对于此方案,客户端将不具有交互式用户,并将使用IdentityServer的所谓客户端密钥进行身份验证。将以下代码添加到Config.cs文件中:

接下来,为您的解决方案添加API。

控制器

向API项目添加新控制器:

组态

最后一步是将身份验证服务添加到DI和身份验证中间件到管道。这些将:

将IdentityServer4.AccessTokenValidationNuGet包添加到项目中。

将Startup更新为如下所示:

就是这样,API现在受到IdentityServer的保护。

IdentityServer的令牌端点实现OAuth2.0协议,您可以使用原始HTTP来访问它。但是,我们有一个名为IdentityModel的客户端库,它将协议交互封装在一个易于使用的API中。

将IdentityModelNuGet包添加到您的应用程序。

IdentityModel包括用于发现端点的客户端库。这样您只需要知道IdentityServer的基地址-可以从元数据中读取实际的端点地址:

接下来,您可以使用该RequestClientCredentialsAsync方法为您的API请求令牌:

//requesttokenvartokenClient=newTokenClient(disco.TokenEndpoint,"client","secret");vartokenResponse=awaittokenClient.RequestClientCredentialsAsync("api1");if(tokenResponse.IsError){Console.WriteLine(tokenResponse.Error);return;}Console.WriteLine(tokenResponse.Json);注意

最后一步是调用API。

要将访问令牌发送到API,通常使用HTTPAuthorization标头。这是使用SetBearerToken扩展方法完成的:

您现在可以尝试激发错误以了解系统的行为,例如

OAuth2.0资源所有者密码授予允许客户端向令牌服务发送用户名和密码,并获取代表该用户的访问令牌。

规范建议仅对“受信任”(或遗留)应用程序使用资源所有者密码授予。一般来说,当您想要对用户进行身份验证并请求访问令牌时,通常会更好地使用其中一个交互式OpenIDConnect流程。

就像资源(也称为范围)和客户端的内存存储一样,用户也有一个。

有关如何正确存储和管理用户帐户的详细信息,请查看基于ASP.NET身份的快速入门。

首先将以下using语句添加到Config.cs文件中:

usingIdentityServer4.Test;publicstaticListGetUsers(){returnnewList{newTestUser{SubjectId="1",Username="alice",Password="password"},newTestUser{SubjectId="2",Username="bob",Password="password"}};}然后使用IdentityServer注册测试用户:

publicvoidConfigureServices(IServiceCollectionservices){//configureidentityserverwithin-memorystores,keys,clientsandscopesservices.AddIdentityServer().AddDeveloperSigningCredential().AddInMemoryApiResources(Config.GetApiResources()).AddInMemoryClients(Config.GetClients()).AddTestUsers(Config.GetUsers());}该AddTestUsers扩展方法做了几件事情引擎盖下

通常,您希望为资源所有者用例创建单独的客户端,将以下内容添加到客户端配置中:

IdentityModel再次TokenClient可以在这里提供帮助:

在本快速入门中,我们希望通过OpenIDConnect协议为我们的IdentityServer添加对交互式用户身份验证的支持。

一旦到位,我们将创建一个将使用IdentityServer进行身份验证的MVC应用程序。

虽然外观和精确的工作流程在每个IdentityServer实现中可能总是不同,但我们提供了一个基于MVC的示例UI,您可以将其用作起点。

或者,您可以从与IdentityServerWeb应用程序相同的目录中的命令行运行此命令,以自动执行下载:

publicvoidConfigureServices(IServiceCollectionservices){services.AddMvc();//configureidentityserverwithin-memorystores,keys,clientsandscopesservices.AddIdentityServer().AddDeveloperSigningCredential().AddInMemoryApiResources(Config.GetApiResources()).AddInMemoryClients(Config.GetClients()).AddTestUsers(Config.GetUsers());}Configure使用UseMvc扩展方法将MVC添加为管道中的最后一个中间件:

releaseUIrepo的分支具有与最新稳定版本匹配的UI。该dev分支与IdentityServer4的当前开发版本一起使用。如果您正在寻找特定版本的UI-请检查标签。

接下来,您将向您的解决方案添加MVC应用程序。使用ASP.NETCore“Web应用程序”(即MVC)模板。不要在向导中配置“身份验证”设置-您将在此快速入门中手动执行此操作。创建项目后,将应用程序配置为使用端口5002(有关如何执行此操作的说明,请参阅概述部分)。

要为ID连接的认证支持添加到了MVC应用程序,添加以下内容ConfigureServices中Startup:

然后AddCookie,我们使用添加可以处理cookie的处理程序。

最后,AddOpenIdConnect用于配置执行OpenIDConnect协议的处理程序。这Authority表明我们信任IdentityServer。然后我们通过ClientId。识别这个客户。SignInScheme用于在OpenIDConnect协议完成后使用cookie处理程序发出cookie。并且SaveTokens用于在cookie中保留来自IdentityServer的令牌(因为稍后将需要它们)。

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();然后要确保认证服务执行对每个请求,加入UseAuthentication到Configure中Startup:

publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv){if(env.IsDevelopment()){app.UseDeveloperExceptionPage();}else{app.UseExceptionHandler("/Home/Error");}app.UseAuthentication();app.UseStaticFiles();app.UseMvcWithDefaultRoute();}应该在管道中的MVC之前添加认证中间件。

@foreach(varclaiminUser.Claims){
@claim.Type
@claim.Value
}
如果您现在使用浏览器导航到该控制器,将尝试重定向到IdentityServer-这将导致错误,因为MVC客户端尚未注册。

与OAuth2.0类似,OpenIDConnect也使用范围概念。同样,范围代表您想要保护的内容以及客户想要访问的内容。与OAuth相比,OIDC中的范围不代表API,而是代表用户ID,名称或电子邮件地址等身份数据。

通过添加新助手(in)来创建对象集合,添加对标准openid(subjectid)和profile(名字,姓氏等)范围的支持:Config.cs``IdentityResource

publicstaticIEnumerableGetIdentityResources(){returnnewList{newIdentityResources.OpenId(),newIdentityResources.Profile(),};}注意

然后,您需要将这些标识资源添加到IdentityServer配置中Startup.cs。使用AddInMemoryIdentityResources您调用的扩展方法AddIdentityServer():

publicvoidConfigureServices(IServiceCollectionservices){services.AddMvc();//configureidentityserverwithin-memorystores,keys,clientsandscopesservices.AddIdentityServer().AddDeveloperSigningCredential().AddInMemoryIdentityResources(Config.GetIdentityResources()).AddInMemoryApiResources(Config.GetApiResources()).AddInMemoryClients(Config.GetClients()).AddTestUsers(Config.GetUsers());}为OpenIDConnect隐式流添加客户端最后一步是将MVC客户端的新配置条目添加到IdentityServer。

基于OpenIDConnect的客户端与我们目前添加的OAuth2.0客户端非常相似。但由于OIDC中的流程始终是交互式的,因此我们需要在配置中添加一些重定向URL。

将以下内容添加到客户端配置中:

可以使用RequireConsent客户端对象上的属性基于每个客户端关闭同意。

在开发期间,您有时可能会看到一个异常,指出无法验证令牌。这是因为签名密钥材料是在运行中创建的,并且仅保留在内存中。当客户端和IdentityServer不同步时会发生此异常。只需在客户端重复操作,下次元数据赶上时,一切都应该再次正常工作。

最后一步是向MVC客户端添加注销。

确切的协议步骤在OpenIDConnect中间件中实现,只需将以下代码添加到某个控制器即可触发注销:

publicasyncTaskLogout(){awaitHttpContext.SignOutAsync("Cookies");awaitHttpContext.SignOutAsync("oidc");}这将清除本地cookie,然后重定向到IdentityServer。IdentityServer将清除其cookie,然后为用户提供返回MVC应用程序的链接。

接下来,我们将添加对外部认证的支持。这非常简单,因为您真正需要的是ASP.NETCore兼容的身份验证处理程序。

如果您在端口5000上运行-您只需使用下面代码段中的客户端ID/secret,因为这是我们预先注册的。

首先将Google身份验证处理程序添加到DI。这是通过添加该代码段完成ConfigureServices的Startup:

publicvoidConfigureServices(IServiceCollectionservices){services.AddMvc();//configureidentityserverwithin-memorystores,keys,clientsandscopesservices.AddIdentityServer().AddDeveloperSigningCredential().AddInMemoryIdentityResources(Config.GetIdentityResources()).AddInMemoryApiResources(Config.GetApiResources()).AddInMemoryClients(Config.GetClients()).AddTestUsers(Config.GetUsers());services.AddAuthentication().AddGoogle("Google",options=>{options.SignInScheme=IdentityServerConstants.ExternalCookieAuthenticationScheme;options.ClientId="434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com";options.ClientSecret="3gcoTrEDPPJ0ukn_aYYT6PWo";});}默认情况下,IdentityServer专门为外部身份验证的结果配置cookie处理程序(使用基于常量的方案IdentityServerConstants.ExternalCookieAuthenticationScheme)。然后,Google处理程序的配置使用该cookie处理程序。为了更好地理解如何完成此操作,请参阅Quickstart文件夹AccountController下的类。

将OpenIdConnect处理程序添加到DI:

在之前的快速入门中,我们探讨了API访问和用户身份验证。现在我们想把这两个部分放在一起。

OpenIDConnect和OAuth2.0组合的优点在于,您可以使用单个协议和使用令牌服务进行单次交换来实现这两者。

在之前的快速入门中,我们使用了OpenIDConnect隐式流程。在隐式流程中,所有令牌都通过浏览器传输,这对于身份令牌来说是完全正确的。现在我们还想要一个访问令牌。

访问令牌比身份令牌更敏感,如果不需要,我们不希望将它们暴露给“外部”世界。OpenIDConnect包含一个名为“混合流”的流程,它为我们提供了两全其美的优势,身份令牌通过浏览器渠道传输,因此客户端可以在进行任何更多工作之前对其进行验证。如果验证成功,客户端会打开令牌服务的反向通道以检索访问令牌。

没有太多必要的修改。首先,我们希望允许客户端使用混合流,此外我们还希望客户端允许执行不在用户上下文中的服务器到服务器API调用(这与我们的客户端凭证快速启动非常相似)。这是使用该AllowedGrantTypes属性表示的。

接下来我们需要添加一个客户端密钥。这将用于检索反向通道上的访问令牌。

最后,我们还让客户端访问offline_access范围-这允许请求刷新令牌以实现长期存在的API访问:

我们配置ClientSecret匹配IdentityServer的秘密。添加offline_access和api1范围,并设置ResponseType为(这基本上意味着“使用混合流”)codeid_token

OpenIDConnect中间件会自动为您保存令牌(在我们的案例中为身份,访问和刷新)。这就是SaveTokens设置的作用。

从技术上讲,令牌存储在cookie的属性部分中。访问它们的最简单方法是使用Microsoft.AspNetCore.Authentication命名空间中的扩展方法。

accesstoken
@awaitViewContext.HttpContext.GetTokenAsync("access_token")
refreshtoken
@awaitViewContext.HttpContext.GetTokenAsync("refresh_token")
要使用访问令牌访问API,您需要做的就是检索令牌,并在HttpClient上设置它:

本快速入门假设您已经完成了所有之前的快速入门。本快速入门使用ASP.NET标识的方法是从VisualStudio中的ASP.NET标识模板创建一个新项目。这个新项目将取代我们在之前的快速入门中从头开始构建的先前IdentityServer项目。此解决方案中的所有其他项目(针对客户端和API)将保持不变。

首先创建一个新的“ASP.NET核心Web应用程序”项目。

然后选择“Web应用程序模板(模型-视图-控制器)”选项。

然后单击“更改身份验证”按钮,并选择“个人用户帐户”(这意味着使用ASP.NET身份):

最后,您的新项目对话框应该如下所示。完成后,单击“确定”以创建项目。

添加IdentityServer4.AspNetIdentityNuGet包。这取决于IdentityServer4包,因此会自动添加为传递依赖项。

必要的配置更改(暂时)是禁用MVC客户端的同意。我们还没有复制先前IdentityServer项目的同意代码,所以现在对MVC客户端进行一次修改并设置RequireConsent=false:

ConfigureServices

这显示了为ASP.NETIdentity生成的模板代码,以及IdentityServer所需的附加内容(最后)。在之前的快速入门中,AddTestUsers扩展方法用于注册用户,但在这种情况下,我们将该扩展方法替换AddAspNetIdentity为使用ASP.NETIdentity用户。该AddAspNetIdentity扩展方法需要一个通用的参数,它是你的ASP.NET身份用户类型(同一个在需要AddIdentity从模板方法)。

publicvoidConfigureServices(IServiceCollectionservices){services.AddDbContext(options=>options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));services.AddIdentity().AddEntityFrameworkStores().AddDefaultTokenProviders();//Addapplicationservices.services.AddTransient();services.AddMvc();//configureidentityserverwithin-memorystores,keys,clientsandscopesservices.AddIdentityServer().AddDeveloperSigningCredential().AddInMemoryPersistedGrants().AddInMemoryIdentityResources(Config.GetIdentityResources()).AddInMemoryApiResources(Config.GetApiResources()).AddInMemoryClients(Config.GetClients()).AddAspNetIdentity();}注意

在使用ASP.NET标识时,在DI系统中在ASP.NET标识之后注册IdentityServer非常重要,因为IdentityServer会从ASP.NET标识覆盖某些配置。

配置

这显示了为ASP.NETIdentity生成的模板代码,以及UseIdentityServer替换调用的调用UseAuthentication。

publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv){if(env.IsDevelopment()){app.UseDeveloperExceptionPage();app.UseBrowserLink();app.UseDatabaseErrorPage();}else{app.UseExceptionHandler("/Home/Error");}app.UseStaticFiles();//app.UseAuthentication();//notneeded,sinceUseIdentityServeraddstheauthenticationmiddlewareapp.UseIdentityServer();app.UseMvc(routes=>{routes.MapRoute(name:"default",template:"{controller=Home}/{action=Index}/{id}");});}创建用户数据库鉴于这是一个新的ASP.NETIdentity项目,您将需要创建数据库。您可以通过从项目目录运行命令提示符并运行来执行此操作,如下所示:dotnetefdatabaseupdate-cApplicationDbContext

此时,您应该能够运行项目并在数据库中创建/注册用户。启动应用程序,然后从主页单击“注册”链接:

在注册页面上创建一个新的用户帐户:

您还应该能够单击“使用应用程序标识调用API”来代表用户调用API:

IdentityServer的先前快速入门项目提供了同意页面,错误页面和注销页面。这些缺失部分的代码可以简单地从之前的快速入门项目复制到此项目中。完成后,您最终可以删除/删除旧的IdentityServer项目。此外,一旦完成此操作,请不要忘记RequireConsent=true在MVC客户端配置上重新启用该标志。

为JavaScript应用程序创建一个新项目。它可以只是一个空的Web项目,也可以是一个空的ASP.NETCore应用程序。此快速入门将使用空的ASP.NETCore应用程序。

创建一个新的ASP.NETCoreWeb应用程序:

选择“空”模板:

单击“确定”按钮以创建项目。

鉴于该项目主要用于客户端,我们需要ASP.NETCore来提供构成我们应用程序的静态HTML和JavaScript文件。静态文件中间件旨在实现此目的。

在方法中注册Startup.cs中的静态文件中间件Configure:

publicvoidConfigure(IApplicationBuilderapp){app.UseDefaultFiles();app.UseStaticFiles();}此中间件现在将从应用程序的/wwwroot文件夹中提供静态文件。这是我们将放置HTML和JavaScript文件的地方。

NPM

如果要使用NPM下载oidc-client,请按照以下步骤操作:

将新的NPM包文件添加到项目中并将其命名为package.json:

在的package.json一个补充dependency到oidc-client:

"dependencies":{"oidc-client":"1.4.1"}保存此文件后,VisualStudio应自动将这些包还原到名为node_modules的文件夹中:

在/node_modules/oidc-client/dist文件夹中找到名为oidc-client.js的文件,并将其复制到应用程序的/wwwroot文件夹中。有更复杂的方法将NPM包复制到/wwwroot,但这些技术超出了本快速入门的范围。

接下来是将您的HTML和JavaScript文件添加到/wwwroot。我们将有两个HTML文件和一个特定于应用程序的JavaScript文件(除了oidc-client.js库)。在/wwwroot中,添加一个名为index.html和callback.html的HTML文件,并添加一个名为app.js的JavaScript文件。

的index.html

它应该如下所示:

LoginCallAPILogoutapp.js

这将包含我们的应用程序的主要代码。第一件事是添加一个帮助函数来将消息记录到

functionlog(){document.getElementById('results').innerText='';Array.prototype.forEach.call(arguments,function(msg){if(msginstanceofError){msg="Error:"+msg.message;}elseif(typeofmsg!=='string'){msg=JSON.stringify(msg,null,2);}document.getElementById('results').innerHTML+=msg+'\r\n';});}接下来,添加代码以将“click”事件处理程序注册到三个按钮:

document.getElementById("login").addEventListener("click",login,false);document.getElementById("api").addEventListener("click",api,false);document.getElementById("logout").addEventListener("click",logout,false);接下来,我们可以使用UserManager类的OIDC客户端库来管理ID连接协议。它需要MVCClient中必需的类似配置(尽管具有不同的值)。添加此代码以配置和实例化UserManager:

客户注册加入IdentityServer的JavaScript客户端既然客户端应用程序已经准备就绪,我们需要在IdentityServer中为这个新的JavaScript客户端定义一个配置条目。在IdentityServer项目中找到客户端配置(在Config.cs中)。将新客户端添加到我们的新JavaScript应用程序的列表中。它应具有下面列出的配置:

配置CORS

ConfigureServices在Startup.cs中将CORS服务添加到依赖注入系统:

publicvoidConfigure(IApplicationBuilderapp){app.UseCors("default");app.UseAuthentication();app.UseMvc();}运行JavaScript应用程序现在您应该能够运行JavaScript客户端应用程序:

然后单击“API”按钮以调用WebAPI:

最后点击“退出”以签署用户。

IdentityServer旨在实现可扩展性,其中一个可扩展点是用于IdentityServer所需数据的存储机制。本快速入门展示了如何配置IdentityServer以使用EntityFramework(EF)作为此数据的存储机制(而不是使用我们迄今为止使用的内存中实现)。

我们正在向数据库移动两种类型的数据。第一个是配置数据(资源和客户端)。第二个是IdentityServer在使用时产生的操作数据(令牌,代码和同意)。这些存储使用接口建模,我们在IdentityServer4.EntityFrameworkNuget包中提供这些接口的EF实现。

通过添加IdentityServer项目的IdentityServer4.EntityFrameworkNuget包的引用开始。

鉴于EF的灵活性,您可以使用任何EF支持的数据库。对于本快速入门,我们将使用VisualStudio附带的SqlServer的LocalDb版本。

根据您为IdentityServer主机创建初始项目的方式,您可能已在csproj文件中配置了这些工具。如果是,您可以跳到下一部分。

然后在结尾元素之前添加以下代码段:

它应该看起来像这样:

保存并关闭文件。要测试您是否正确安装了这些工具,可以在与项目相同的目录中打开命令shell并运行dotnetef。它应该如下所示:

接下来的步骤是,以取代当前呼叫AddInMemoryClients,AddInMemoryIdentityResources和AddInMemoryApiResources在ConfigureServices在方法Startup.cs。我们将使用以下代码替换它们:

conststringconnectionString=@"DataSource=(LocalDb)\MSSQLLocalDB;database=IdentityServer4.Quickstart.EntityFramework-2.0.0;trusted_connection=yes;";varmigrationsAssembly=typeof(Startup).GetTypeInfo().Assembly.GetName().Name;//configureidentityserverwithin-memorystores,keys,clientsandscopesservices.AddIdentityServer().AddDeveloperSigningCredential().AddTestUsers(Config.GetUsers())//thisaddstheconfigdatafromDB(clients,resources).AddConfigurationStore(options=>{options.ConfigureDbContext=builder=>builder.UseSqlServer(connectionString,sql=>sql.MigrationsAssembly(migrationsAssembly));})//thisaddstheoperationaldatafromDB(codes,tokens,consents).AddOperationalStore(options=>{options.ConfigureDbContext=builder=>builder.UseSqlServer(connectionString,sql=>sql.MigrationsAssembly(migrationsAssembly));//thisenablesautomatictokencleanup.thisisoptional.options.EnableTokenCleanup=true;options.TokenCleanupInterval=30;});您可能需要将这些命名空间添加到文件中:

usingMicrosoft.EntityFrameworkCore;usingSystem.Reflection;上面的代码是对连接字符串进行硬编码,如果您愿意,可以随意更改。此外,调用AddConfigurationStore和AddOperationalStore注册EF支持的商店实现。

传递给这些API的“构建器”回调函数是EF机制,允许您为这两个存储中的每一个配置DbContextOptionsBuilderforDbContext。这就是我们的DbContext类可以使用您要使用的数据库提供程序进行配置的方式。在这种情况下,通过调用UseSqlServer我们正在使用SqlServer。您也可以看出,这是提供连接字符串的位置。

“options”回调函数用于UseSqlServer配置定义EF迁移的程序集。EF需要使用迁移来定义数据库的模式。

托管应用程序负责定义这些迁移,因为它们特定于您的数据库和提供程序。

我们接下来会添加迁移。

要创建迁移,请在IdentityServer项目目录中打开命令提示符。在命令提示符下运行以下两个命令:

dotnetefmigrationsaddInitialIdentityServerPersistedGrantDbMigration-cPersistedGrantDbContext-oData/Migrations/IdentityServer/PersistedGrantDbdotnetefmigrationsaddInitialIdentityServerConfigurationDbMigration-cConfigurationDbContext-oData/Migrations/IdentityServer/ConfigurationDb它应该看起来像这样:

您现在应该在项目中看到/Data/Migrations/IdentityServer文件夹。其中包含新创建的迁移的代码。

现在我们已经进行了迁移,我们可以编写代码来从迁移中创建数据库。我们还将使用我们在之前的快速入门中定义的内存配置数据来为数据库设定种子。

在Startup.cs中添加此方法以帮助初始化数据库:

privatevoidInitializeDatabase(IApplicationBuilderapp){using(varserviceScope=app.ApplicationServices.GetService().CreateScope()){serviceScope.ServiceProvider.GetRequiredService().Database.Migrate();varcontext=serviceScope.ServiceProvider.GetRequiredService();context.Database.Migrate();if(!context.Clients.Any()){foreach(varclientinConfig.GetClients()){context.Clients.Add(client.ToEntity());}context.SaveChanges();}if(!context.IdentityResources.Any()){foreach(varresourceinConfig.GetIdentityResources()){context.IdentityResources.Add(resource.ToEntity());}context.SaveChanges();}if(!context.ApiResources.Any()){foreach(varresourceinConfig.GetApiResources()){context.ApiResources.Add(resource.ToEntity());}context.SaveChanges();}}}然后我们可以从Configure方法中调用它:

publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv,ILoggerFactoryloggerFactory){//thiswilldotheinitialDBpopulationInitializeDatabase(app);//therestofthecodethatwasalreadyhere//...}现在,如果运行IdentityServer项目,则应创建数据库并使用快速入门配置数据进行种子设定。您应该能够使用SQLServerManagementStudio或VisualStudio来连接和检查数据。

上面的InitializeDatabase辅助API可以方便地为数据库设定种子,但是这种方法并不适合每次运行应用程序时执行。填充数据库后,请考虑删除对API的调用。

本节中的代码仍然依赖于Config.cs及其虚构用户Alice和Bob。如果您的用户列表很简短且静态,则调整后的Config.cs版本可能就足够了,但您可能希望在数据库中动态管理更大且更流畅的用户列表。ASP.NETIdentity是一个需要考虑的选项,下一节的快速入门列出了此解决方案的示例实现。

IdentityServer组织不维护这些示例。IdentityServer组织愉快地链接到社区样本,但不能对样本做出任何保证。请直接与作者联系。

此示例显示如何在与保护API的IdentityServer相同的主机中托管API。

IdentityServer是中间件和服务的组合。所有配置都在您的启动类中完成。

您可以通过调用以下方法将IdentityServer服务添加到DI系统:

这将返回一个构建器对象,该构建器对象又有许多方便的方法来连接其他服务。

各种“内存中”配置API允许从内存中的配置对象列表配置IdentityServer。这些“内存中”集合可以在宿主应用程序中进行硬编码,也可以从配置文件或数据库动态加载。但是,通过设计,这些集合仅在托管应用程序启动时创建。

使用这些配置API的目的是在原型设计,开发和/或测试时使用,在这种情况下,不需要在运行时为配置数据动态查询数据库。如果配置很少更改,则此配置样式也可能适用于生产方案,或者如果必须更改值,则要求重新启动应用程序并不方便。

该TestUser级车型的用户,他们的凭据,并在IdentityServer索赔。使用TestUser“内存”商店是因为它适用于原型设计,开发和/或测试。采用TestUser在生产中不推荐使用。

IdentityServer经常使用客户端和资源配置数据。如果从数据库或其他外部存储加载此数据,则频繁重新加载相同数据可能会很昂贵。

可以进一步自定义缓存:

默认缓存依赖于ICache实现。如果要自定义特定配置对象的缓存行为,可以在依赖项注入系统中替换此实现。

ICache本身的默认实现依赖于.NET提供的IMemoryCache接口(和MemoryCache实现)。如果要自定义内存中缓存行为,可以替换IMemoryCache依赖项注入系统中的实现。

您需要通过调用以下方法将IdentityServer添加到管道:

publicvoidConfigure(IApplicationBuilderapp){app.UseIdentityServer();}注意

中间件没有其他配置。

您通常在系统中定义的第一件事是您要保护的资源。这可能是您的用户的身份信息,如个人资料数据或电子邮件地址,或访问API。

您可以使用C#对象模型定义资源-或从数据存储加载它们。的实施IResourceStore与这些低级别的细节交易。对于本文档,我们使用内存中实现。

要允许客户端请求API的访问令牌,您需要定义API资源,例如:

要获取API的访问权限,您还需要将它们注册为范围。这次范围类型是Resource类型:

客户端表示可以从您的身份服务器请求令牌的应用程序。

详细信息各不相同,但您通常会为客户端定义以下常用设置:

在运行时,通过实现来检索客户端IClientStore。这允许从任意数据源(如配置文件或数据库)加载它们。对于本文档,我们将使用客户端存储的内存版本。您可以ConfigureServices通过AddInMemoryClientsextensions方法连接内存存储。

在这种情况下,没有交互式用户-服务(也称为客户端)想要与API(aka范围)进行通信:

publicclassClients{publicstaticIEnumerableGet(){returnnewList{newClient{ClientId="service.client",ClientSecrets={newSecret("secret".Sha256())},AllowedGrantTypes=GrantTypes.ClientCredentials,AllowedScopes={"api1","api2.read_only"}}};}}定义基于浏览器的JavaScript客户端(例如SPA)以进行用户身份验证和委派访问以及此客户端使用所谓的隐式流来从JavaScript请求身份和访问令牌:

交互式服务器端(或本机桌面/移动)应用程序使用混合流。此流程为您提供最佳安全性,因为访问令牌仅通过反向通道调用传输(并允许您访问刷新令牌):

IdentityServer注册了两个cookie处理程序(一个用于身份验证会话,另一个用于临时外部cookie)。默认情况下使用它们,如果要手动引用它们,可以从IdentityServerConstants类(DefaultCookieAuthenticationScheme和ExternalCookieAuthenticationScheme)中获取它们的名称。

我们只公开这些cookie的基本设置(到期和滑动),如果您需要更多控制,您可以注册自己的cookie处理程序。IdentityServer使用与使用ASP.NETCore时DefaultAuthenticateScheme配置的cookie处理程序相匹配的cookie处理程序。AuthenticationOptions``AddAuthentication

如果您希望使用自己的cookie身份验证处理程序,则必须自己配置它。这必须ConfigureServices在DI(withAddIdentityServer)中注册IdentityServer之后完成。例如:

services.AddIdentityServer().AddInMemoryClients(Clients.Get()).AddInMemoryIdentityResources(Resources.GetIdentityResources()).AddInMemoryApiResources(Resources.GetApiResources()).AddDeveloperSigningCredential().AddTestUsers(TestUsers.Users);services.AddAuthentication("MyCookie").AddCookie("MyCookie",options=>{options.ExpireTimeSpan=...;});注意

IdentityServer在内部调用两个AddAuthentication并AddCookie使用自定义方案(通过常量IdentityServerConstants.DefaultCookieAuthenticationScheme),因此要覆盖它们,您必须在之后进行相同的调用AddIdentityServer。

IdentityServer不为用户身份验证提供任何用户界面或用户数据库。这些是您希望自己提供或发展的东西。

当您签署用户时,您必须至少发出sub索赔和name索赔。IdentityServer还提供了一些SignInAsync扩展方法HttpContext,使其更加方便。

ASP.NETCore有一种灵活的方式来处理外部身份验证。这涉及几个步骤。

与外部提供者通信所需的协议实现封装在身份验证处理程序中。一些提供商使用专有协议(例如Facebook等社交提供商),有些提供商使用标准协议,例如OpenIDConnect,WS-Federation或SAML2p。

调用外部身份验证处理程序的一个选项SignInScheme,例如:

鉴于这是一种常见做法,IdentityServer专门为此外部提供程序工作流注册cookie处理程序。该方案通过IdentityServerConstants.ExternalCookieAuthenticationScheme常数表示。如果您要使用我们的外部cookie处理程序,那么对于SignInScheme上面的内容,您将赋值为IdentityServerConstants.ExternalCookieAuthenticationScheme常量:

services.AddAuthentication().AddGoogle("Google",options=>{options.SignInScheme=IdentityServerConstants.ExternalCookieAuthenticationScheme;options.ClientId="...";options.ClientSecret="...";})您也可以注册自己的自定义cookie处理程序,如下所示:

services.AddAuthentication().AddCookie("YourCustomScheme").AddGoogle("Google",options=>{options.SignInScheme="YourCustomScheme";options.ClientId="...";options.ClientSecret="...";})注意

您可以通过(或使用MVC)ChallengeAsync上的扩展方法调用外部认证处理程序。HttpContext``ChallengeResult

您通常希望将一些选项传递给挑战操作,例如回调页面的路径和簿记提供者的名称,例如:

varcallbackUrl=Url.Action("ExternalLoginCallback");varprops=newAuthenticationProperties{RedirectUri=callbackUrl,Items={{"scheme",provider},{"returnUrl",returnUrl}}};returnChallenge(provider,props);处理回调并签署用户在回调页面上,您的典型任务是:

检查外部身份:

幸运的是,IdentityServer为您提供了一个实现,由IDistributedCacheDI容器中注册的实现(例如标准MemoryDistributedCache)支持。要使用IdentityServer提供的安全数据格式实现,只需在配置DI时调用AddOidcStateDataFormatterCache扩展方法IServiceCollection。如果未传递任何参数,则配置的所有OpenIDConnect处理程序将使用IdentityServer提供的安全数据格式实现:

publicvoidConfigureServices(IServiceCollectionservices){//configurestheOpenIdConnecthandlerstopersistthestateparameterintotheserver-sideIDistributedCache.services.AddOidcStateDataFormatterCache();services.AddAuthentication().AddOpenIdConnect("demoidsrv","IdentityServer",options=>{//...}).AddOpenIdConnect("aad","AzureAD",options=>{//...}).AddOpenIdConnect("adfs","ADFS",options=>{//...});}如果只配置特定方案,则将这些方案作为参数传递:

publicvoidConfigureServices(IServiceCollectionservices){//configurestheOpenIdConnecthandlerstopersistthestateparameterintotheserver-sideIDistributedCache.services.AddOidcStateDataFormatterCache("aad","demoidsrv");services.AddAuthentication().AddOpenIdConnect("demoidsrv","IdentityServer",options=>{//...}).AddOpenIdConnect("aad","AzureAD",options=>{//...}).AddOpenIdConnect("adfs","ADFS",options=>{//...});}Windows身份验证在支持的平台上,您可以使用IdentityServer对使用Windows身份验证的用户进行身份验证(例如,针对ActiveDirectory)。当前使用以下命令托管IdentityServer时,Windows身份验证可用:

使用Kestrel时,必须运行“后面”IIS并使用IIS集成:

IIS(或IISExpress)中的虚拟目录也必须启用Windows并启用匿名身份验证。

IIS集成层将Windows身份验证处理程序配置为DI,可以通过身份验证服务调用。通常在IdentityServer中,建议禁用此自动行为。这是在ConfigureServices:

services.Configure(iis=>{iis.AuthenticationDisplayName="Windows";iis.AutomaticAuthentication=false;});注意

默认情况下,显示名称为空,Windows身份验证按钮不会显示在快速入门UI中。如果依赖于自动发现外部提供程序,则需要设置显示名称。

注销IdentityServer就像删除身份验证cookie一样简单,但是为了完成联合注销,我们必须考虑将用户从客户端应用程序(甚至可能是上游身份提供商)中签名。

要删除身份验证cookie,只需使用SignOutAsync扩展方法即可HttpContext。您将需要传递使用的方案(IdentityServerConstants.DefaultCookieAuthenticationScheme除非您已更改,否则提供此方案):

awaitHttpContext.SignOutAsync(IdentityServerConstants.DefaultCookieAuthenticationScheme);或者您可以使用IdentityServer提供的便捷扩展方法:

awaitHttpContext.SignOutAsync();注意

通常,您应该提示用户注销(意味着需要POST),否则攻击者可能会链接到您的注销页面,导致用户自动注销。

前端服务器端客户端

反向通道服务器端客户端

要通过反向通道规范从服务器端客户端应用程序注销用户SignOutIFrameUrl,IdentityServer中的端点将自动触发服务器到服务器调用,将签名的注销请求传递给客户端。这意味着即使如果没有前面通道的客户端中,“退出”,在IdentityServer页仍必须渲染