IdentityServer4是ASP.NET Core 2的OpenID Connect和OAuth 2.0框架。javascript
它在您的应用程序中启用如下功能:html
适用于全部应用程序(Web,本机,移动设备,服务)的集中登陆逻辑和工做流程。IdentityServer是OpenID Connect 的官方认证实现。前端
在多种应用程序类型上单点登陆(和退出)。html5
为各类类型的客户端发出API访问令牌,例如服务器到服务器,Web应用程序,SPA和本机/移动应用程序。java
支持Azure Active Directory,Google,Facebook等外部身份提供商。这能够保护您的应用程序免受如何链接到这些外部提供商的详细信息的影响。node
最重要的部分 - IdentityServer的许多方面均可以根据您的需求进行定制。因为IdentityServer是一个框架而不是盒装产品或SaaS,所以您能够编写代码以使系统适应您的方案。git
IdentityServer使用容许的Apache 2许可证,容许在其上构建商业产品。它也是.NET Foundation的一部分,它提供治理和法律支持。github
若是您须要帮助构建或运行您的身份平台,请告知咱们。咱们能够经过多种方式为您提供帮助。web
大多数现代应用程序或多或少看起来像这样:算法
最多见的互动是:
浏览器与Web应用程序通讯
Web应用程序与Web API进行通讯(有时是本身的,有时是表明用户)
基于浏览器的应用程序与Web API通讯
本机应用程序与Web API通讯
基于服务器的应用程序与Web API通讯
Web API与Web API进行通讯(有时是本身的,有时是表明用户)
一般,每一个层(前端,中间层和后端)都必须保护资源并实现身份验证和/或受权 - 一般针对同一个用户存储。
将这些基本安全功能外包给安全令牌服务可防止在这些应用程序和端点之间复制该功能。
重构应用程序以支持安全令牌服务会产生如下体系结构和协议:
这种设计将安全问题分为两部分:
当应用程序须要知道当前用户的身份时,须要进行身份验证。一般,这些应用程序表明该用户管理数据,而且须要确保该用户只能访问容许的数据。最多见的例子是(经典)Web应用程序 - 可是基于本机和JS的应用程序也须要身份验证。
最多见的身份验证协议是SAML2p,WS-Federation和OpenID Connect - SAML2p是最受欢迎和最普遍部署的。
OpenID Connect是三者中的最新产品,但被认为是将来,由于它具备最大的现代应用潜力。它是从一开始就为移动应用场景而构建的,旨在实现API友好。
应用程序有两种与API通讯的基本方式 - 使用应用程序标识或委派用户的标识。有时两种方法都须要结合起来。
OAuth2是一种协议,容许应用程序从安全令牌服务请求访问令牌并使用它们与API通讯。此委派下降了客户端应用程序和API的复杂性,由于身份验证和受权能够集中。
OpenID Connect和OAuth 2.0很是类似 - 事实上,OpenID Connect是OAuth 2.0之上的扩展。两个基本的安全问题,即身份验证和API访问,被合并为一个协议 - 一般只须要一次往返安全令牌服务。
咱们相信,OpenID Connect和OAuth 2.0的结合是在可预见的将来保护现代应用程序的最佳方法。IdentityServer4是这两种协议的实现,通过高度优化,能够解决当今移动,本机和Web应用程序的典型安全问题。
IdentityServer是一个中间件,可将符合规范的OpenID Connect和OAuth 2.0端点添加到任意ASP.NET Core应用程序中。
一般,您构建(或重用)包含登陆和注销页面的应用程序(而且可能赞成 - 取决于您的须要),IdentityServer中间件为其添加必要的协议头,以便客户端应用程序能够与之通讯使用那些标准协议。
托管应用程序能够像您想要的那样复杂,但咱们一般建议经过仅包含与身份验证相关的UI来使攻击面尽量小。
规范,文档和对象模型使用您应该注意的某些术语。
IdentityServer是OpenID Connect提供程序 - 它实现OpenID Connect和OAuth 2.0协议。
不一样的文献对同一个角色使用不一样的术语 - 您可能还会找到安全令牌服务,身份提供者,受权服务器,IP-STS等。
但它们彻底相同:一种向客户发放安全令牌的软件。
IdentityServer具备许多做业和功能 - 包括:
保护你的资源
使用本地账户存储或外部身份提供程序对用户进行身份验证
提供会话管理和单点登陆
管理和验证客户端
向客户发放身份和访问令牌
验证令牌
用户是使用注册客户端访问资源的人。
客户端是从IdentityServer请求令牌的软件 - 用于验证用户(请求身份令牌)或访问资源(请求访问令牌)。客户端必须首先向IdentityServer注册,而后才能请求令牌。
客户端的示例包括Web应用程序,本机移动或桌面应用程序,SPA,服务器进程等。
您但愿使用IdentityServer保护资源 - 用户的身份数据或API。
每一个资源都有一个惟一的名称 - 客户端使用此名称来指定他们但愿访问哪些资源。
身份数据 关于用户的身份信息(也称为声明),例如姓名或电子邮件地址。
API API资源表示客户端要调用的功能 - 一般建模为Web API,但不必定。
身份令牌表示身份验证过程的结果。它至少包含用户的标识符(称为sub aka subject声明)以及有关用户如何以及什么时候进行身份验证的信息。它能够包含其余身份数据。
访问令牌容许访问API资源。客户端请求访问令牌并将其转发给API。访问令牌包含有关客户端和用户(若是存在)的信息。API使用该信息来受权访问其数据。
IdentityServer实现如下规范:
OpenID Connect Core 1.0(规范)
OpenID Connect Discovery 1.0(规范)
OpenID Connect会话管理1.0 - 草案28(规范)
OpenID Connect Front-Channel Logout 1.0 - 草案02(规范)
OpenID Connect Back-Channel Logout 1.0 - 草案04(规范)
OAuth 2.0(RFC 6749)
OAuth 2.0承载令牌使用(RFC 6750)
OAuth 2.0多种响应类型(规范)
OAuth 2.0表单后期响应模式(规范)
OAuth 2.0令牌撤销(RFC 7009)
OAuth 2.0令牌自省(RFC 7662)
代码交换的证实密钥(RFC 7636)
用于客户端身份验证的JSON Web令牌(RFC 7523)
IdentityServer由许多nuget包组成。
包含核心IdentityServer对象模型,服务和中间件。仅包含对内存配置和用户存储的支持 - 但您能够经过配置插入对其余存储的支持。这是其余回购和包装的内容。
包含一个简单的入门UI,包括登陆,注销和赞成页面。
用于验证API中令牌的ASP.NET Core身份验证处理程序。处理程序容许在同一API中支持JWT和引用令牌。
IdentityServer的ASP.NET核心身份集成包。该软件包提供了一个简单的配置API,能够为IdentityServer用户使用ASP.NET身份管理库。
EntityFramework IdentityServer的核心存储实现。此程序包为IdentityServer中的配置和操做存储提供EntityFramework实现。
此外,咱们将开发/临时构建发布到MyGet。若是要尝试尝试,请将如下Feed添加到Visual Studio:
https://www.myget.org/F/identity/
咱们为IdentityServer提供了多种免费和商业支持和咨询选项。
免费支持是基于社区的,并使用公共论坛
堆栈溢出
有愈来愈多的人使用IdentityServer来监控StackOverflow上的问题。若是时间容许,咱们也会尝试回答尽量多的问题
您可使用此Feed订阅全部IdentityServer4相关问题:
https://stackoverflow.com/questions/tagged/?tagnames=identityserver4&sort=newest
IdentityServer4
在提出新问题时请使用标签
小胶质
您能够在咱们的Gitter聊天室中与其余IdentityServer4用户聊天:
https://gitter.im/IdentityServer/IdentityServer4
报告错误
若是您认为本身发现了错误或意外行为,请在Github 问题跟踪器上打开一个问题。咱们会尽快回复您。请理解咱们也有平常工做,可能太忙而没法当即回复。
在发布以前还要检查贡献指南。
咱们正在围绕身份和访问控制架构进行咨询,指导和定制软件开发,特别是IdentityServer。请取得联系与咱们共同探讨可行方案。
训练
咱们常常围绕现代应用的身份和访问控制进行研讨会。在这里查看议程和即将公布的日期 。咱们也能够在贵公司私下进行培训。 联系咱们以请求现场培训。
Admin UI,Identity Express和SAML2p支持
咱们的合做伙伴提供了几种商业附加产品,请访问https://www.identityserver.com/products/。
您可使用您喜欢的客户端库尝试IdentityServer4。咱们在demo.identityserver.io上有一个测试实例。在主页面上,您能够找到有关如何配置客户端以及如何调用API的说明。
此外,咱们还有一个repo,能够运行各类IdentityServer和Web API组合(IdentityServer 3和4,ASP.NET Core和Katana)。咱们使用此测试工具确保全部排列都有效。您能够经过克隆此 repo来自行测试。
咱们对社区贡献很是开放,但您应该遵循一些指导方针,以便咱们能够绝不费力地处理这个问题。
最简单的贡献方式是打开一个问题并开始讨论。而后咱们能够决定是否以及如何实现功能或更改。若是您应该提交带有代码更改的pull请求,请从描述开始,只进行最小的更改并提供涵盖这些更改的测试。
首先阅读:成为一名优秀的开源公民
请开始讨论核心回购问题跟踪器。
IdentityServer是针对ASP.NET Core 2构建的,可在.NET Framework 4.6.1(及更高版本)和.NET Core 2(及更高版本)上运行。
请在相应的GitHub仓库中记录一个新问题:
https://gitter.im/IdentityServer/IdentityServer4
在您提供任何代码或内容以前,您须要签署贡献者许可协议。这是一个自动过程,将在您打开拉取请求后启动。
注意
咱们只接受开发分支的PR。
若是您启动贡献项目(例如,支持Database X或Configuration Store Y),咱们很是感谢。告诉咱们,咱们能够在咱们的文档中发推文和连接。
咱们一般不想拥有这些贡献库,咱们已经很是忙于支持核心项目。
命名约定
截至2017年10月,IdentityServer4。* nuget名称空间保留给咱们的软件包。请使用如下命名约定:
YourProjectName.IdentityServer4
要么
IdentityServer4.Contrib.YourProjectName
启动新IdentityServer项目有两种基本方法:
白手起家
从Visual Studio中的ASP.NET标识模板开始
若是您从头开始,咱们提供了几个帮助程序和内存存储,所以您没必要担忧从一开始就存在持久性。
若是您从ASP.NET身份开始,咱们也提供了一种简单的方法来集成它。
快速入门提供了各类常见IdentityServer方案的分步说明。他们从绝对的基础开始,变得更加复杂 - 建议你按顺序完成它们。
每一个快速入门都有一个参考解决方案 - 您能够 在quickstarts文件夹中的IdentityServer4.Samples仓库中找到代码 。
屏幕截图显示了Visual Studio - 但这不是必需的。
建立快速入门IdentityServer
首先建立一个新的ASP.NET Core项目。
而后选择“清空”选项。
接下来,添加IdentityServer4 nuget包:
或者,您可使用程序包管理器控制台经过运行如下命令来添加依赖项:
“安装包IdentityServer4”
注意
IdentityServer构建编号1.x目标ASP.NET Core 1.1,IdentityServer构建编号2.x目标ASP.NET Core 2.0。
IdentityServer使用一般的模式为ASP.NET Core主机配置和添加服务。在ConfigureServices
所需的服务中配置并添加到DI系统。在Configure
中间件中添加到HTTP管道。
将Startup.cs
文件修改成以下所示:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddDeveloperSigningCredential();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
}
}
AddIdentityServer
在DI中注册IdentityServer服务。它还为运行时状态注册内存存储。这对于开发方案颇有用。对于生产方案,您须要一个持久性或共享存储,如数据库或缓存。有关详细信息,请参阅EntityFramework快速入门。
该AddDeveloperSigningCredential
扩展程序为签名令牌建立临时密钥材料。一样,这可能对入门有用,但须要替换为生产场景的一些持久性密钥材料。有关更多信息,请参阅加密文档。
注意
IdentityServer还没有准备好启动。咱们将在如下快速入门中添加所需的服务。
默认状况下,Visual Studio使用IIS Express来托管您的Web项目。这彻底没问题,除了您将没法看到控制台的实时日志输出。
IdentityServer普遍使用日志记录,而UI中的“可见”错误消息或返回给客户端是故意模糊的。
咱们建议在控制台主机中运行IdentityServer。您能够经过在Visual Studio中切换启动配置文件来完成此操做。每次启动IdentityServer时也不须要启动浏览器 - 您也能够关闭它:
此外,在这些快速入门的一致URL上运行IdentityServer会颇有帮助。您还应该在上面的启动配置文件对话框中配置此URL,而后使用http://localhost:5000/
。在上面的屏幕截图中,您能够看到此URL已配置。
注意
咱们建议为IIS Express和自托管配置相同的端口。这样,您能够在二者之间切换,而无需修改客户端中的任何配置。
要在启动时选择控制台主机,必须在Visual Studio的启动菜单中选择它:
如上所述,每一个快速入门都有一个参考解决方案 - 您能够 在quickstarts文件夹中的IdentityServer4.Samples repo中找到代码 。
运行快速入门解决方案各个部分的最简单方法是将启动模式设置为“当前选择”。右键单击解决方案并选择“设置启动项目”:
一般,首先启动IdentityServer,而后启动API,而后启动客户端。若是您确实想要调试,只能在调试器中运行。不然Ctrl + F5是运行项目的最佳方式。
本快速入门介绍了使用IdentityServer保护API的最基本方案。
在这种状况下,咱们将定义一个API和一个想要访问它的客户端。客户端将在IdentityServer请求访问令牌并使用它来获取对API的访问权限。
范围定义了您要保护的系统中的资源,例如API。
因为咱们在本演练中使用内存配置 - 您只需建立一个类型的对象ApiResource
并设置适当的属性便可。
将文件(例如Config.cs
)添加到项目中并添加如下代码:
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API")
};
}
下一步是定义能够访问此API的客户端。
对于此方案,客户端将不具备交互式用户,并将使用IdentityServer的所谓客户端密钥进行身份验证。将如下代码添加到Config.cs文件中:
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" }
}
};
}
要将IdentityServer配置为使用范围和客户端定义,您须要向ConfigureServices
方法添加代码。您可使用方便的扩展方法 - 在封面下,这些将相关的存储和数据添加到DI系统中:
public void ConfigureServices(IServiceCollection services)
{
// configure identity server with in-memory stores, keys, clients and resources
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients());
}
就是这样 - 若是您运行服务器并浏览浏览器 http://localhost:5000/.well-known/openid-configuration
,您应该会看到所谓的发现文档。客户端和API将使用它来下载必要的配置数据。
接下来,为您的解决方案添加API。
您可使用ASP.NET Core Web API模板。一样,咱们建议您控制端口并使用与之前配置Kestrel和启动配置文件相同的技术。本演练假定您已将API配置为运行http://localhost:5001
。
控制器
向API项目添加新控制器:
[Route("identity")]
[Authorize]
public class IdentityController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
稍后将使用此控制器来测试受权要求,以及经过API的眼睛可视化声明身份。
组态
最后一步是将身份验证服务添加到DI和身份验证中间件到管道。这些将:
验证传入令牌以确保它来自受信任的颁发者
验证令牌是否有效用于此api(aka范围)
将IdentityServer4.AccessTokenValidation NuGet包添加到项目中。
将Startup更新为以下所示:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "api1";
});
}
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvc();
}
}
AddAuthentication
将身份验证服务添加到DI并配置"Bearer"
为默认方案。AddIdentityServerAuthentication
将IdentityServer访问令牌验证处理程序添加到DI中以供身份验证服务使用。 UseAuthentication
将身份验证中间件添加到管道中,以便在每次调用主机时自动执行身份验证。
若是您使用浏览器导航到控制器(http://localhost:5001/identity
),您应该得到401状态代码做为回报。这意味着您的API须要凭据。
就是这样,API如今受到IdentityServer的保护。
最后一步是编写请求访问令牌的客户端,而后使用此令牌访问API。为此,请向您的解决方案添加一个控制台项目(请参阅此处的完整代码)。
IdentityServer的令牌端点实现OAuth 2.0协议,您可使用原始HTTP来访问它。可是,咱们有一个名为IdentityModel的客户端库,它将协议交互封装在一个易于使用的API中。
将IdentityModel NuGet包添加到您的应用程序。
IdentityModel包括用于发现端点的客户端库。这样您只须要知道IdentityServer的基地址 - 能够从元数据中读取实际的端点地址:
// discover endpoints from metadata
var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
接下来,您可使用TokenClient
该类来请求令牌。要建立实例,您须要传递令牌端点地址,客户端ID和密码。
接下来,您可使用该RequestClientCredentialsAsync
方法为您的API请求令牌:
// request token
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);
注意
将访问令牌从控制台复制并粘贴到jwt.io以检查原始令牌。
最后一步是调用API。
要将访问令牌发送到API,一般使用HTTP Authorization标头。这是使用SetBearerToken
扩展方法完成的:
// call api
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);
var response = await client.GetAsync("http://localhost:5001/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
输出应以下所示:
注意
默认状况下,访问令牌将包含有关范围,生命周期(nbf和exp),客户端ID(client_id)和颁发者名称(iss)的声明。
本演练重点关注目前的成功之路
客户端可以请求令牌
客户端可使用令牌来访问API
您如今能够尝试激发错误以了解系统的行为,例如
尝试在未运行时链接到IdentityServer(不可用)
尝试使用无效的客户端ID或机密来请求令牌
尝试在令牌请求期间请求无效范围
尝试在API未运行时调用API(不可用)
不要将令牌发送到API
将API配置为须要与令牌中的范围不一样的范围
OAuth 2.0资源全部者密码授予容许客户端向令牌服务发送用户名和密码,并获取表明该用户的访问令牌。
规范建议仅对“受信任”(或遗留)应用程序使用资源全部者密码授予。通常来讲,当您想要对用户进行身份验证并请求访问令牌时,一般会更好地使用其中一个交互式OpenID Connect流程。
尽管如此,这种受权类型容许咱们将用户的概念引入咱们的快速启动IdentityServer,这就是咱们展现它的缘由。
就像资源(也称为范围)和客户端的内存存储同样,用户也有一个。
注意
有关如何正确存储和管理用户账户的详细信息,请查看基于ASP.NET身份的快速入门。
该类TestUser
表明测试用户及其声明。让咱们经过在config类中添加如下代码来建立几个用户:
首先将如下using语句添加到Config.cs
文件中:
using IdentityServer4.Test;
public static List<TestUser> GetUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "password"
},
new TestUser
{
SubjectId = "2",
Username = "bob",
Password = "password"
}
};
}
而后使用IdentityServer注册测试用户:
public void ConfigureServices(IServiceCollection services)
{
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
}
该AddTestUsers
扩展方法作了几件事情引擎盖下
添加对资源全部者密码授予的支持
添加对登陆UI一般使用的用户相关服务的支持(咱们将在下一个快速入门中使用它)
添加对基于测试用户的配置文件服务的支持(您将在下一个快速入门中了解更多信息)
您能够经过更改AllowedGrantTypes
属性来简单地向现有客户端添加对受权类型的支持 。若是您须要您的客户端可以使用绝对支持的两种受权类型。
一般,您但愿为资源全部者用例建立单独的客户端,将如下内容添加到客户端配置中:
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
// other clients omitted...
// resource owner password grant client
new Client
{
ClientId = "ro.client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "api1" }
}
};
}
客户端看起来与咱们为客户端凭据授予所作的很是类似。主要区别在于客户端会以某种方式收集用户的密码,并在令牌请求期间将其发送到令牌服务。
IdentityModel再次TokenClient
能够在这里提供帮助:
// request token
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("alice", "password", "api1");
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");
将令牌发送到身份API端点时,您会注意到与客户端凭据受权相比有一个小但重要的区别。访问令牌如今将包含sub
惟一标识用户的声明。经过在调用API以后检查内容变量能够看到这个“子”声明,而且控制器应用程序也会在屏幕上显示该声明。
声明的存在(或不存在)sub
容许API区分表明客户的呼叫和表明用户的呼叫。
在本快速入门中,咱们但愿经过OpenID Connect协议为咱们的IdentityServer添加对交互式用户身份验证的支持。
一旦到位,咱们将建立一个将使用IdentityServer进行身份验证的MVC应用程序。
OpenID Connect所需的全部协议支持已内置于IdentityServer中。您须要为登陆,注销,赞成和错误提供必要的UI部件。
虽然外观和精确的工做流程在每一个IdentityServer实现中可能老是不一样,但咱们提供了一个基于MVC的示例UI,您能够将其用做起点。
能够在快速入门UI存储库中找到此UI 。您能够克隆或下载此repo,并将控制器,视图,模型和CSS放入IdentityServer Web应用程序中。
或者,您能够从与IdentityServer Web应用程序相同的目录中的命令行运行此命令,以自动执行下载:
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.ps1'))
对于Unix / Linux:
\curl -L https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/release/get.sh | bash
添加MVC UI资产后,您还须要在DI系统和管道中将MVC添加到托管应用程序。ConfigureServices
使用AddMvc
扩展方法添加MVC :
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
}
Configure
使用UseMvc
扩展方法将MVC添加为管道中的最后一个中间件:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
有关详细信息,请参阅快速入门UI 的自述文件。
注意
release
UI repo 的分支具备与最新稳定版本匹配的UI。该dev
分支与IdentityServer4的当前开发版本一块儿使用。若是您正在寻找特定版本的UI - 请检查标签。
花一些时间检查控制器和模型,您越了解它们,就越容易进行将来的修改。大多数代码使用“功能文件夹”样式存在于“Quickstart”文件夹中。若是此样式不适合您,请随意以您想要的任何方式组织代码。
接下来,您将向您的解决方案添加MVC应用程序。使用ASP.NET Core“Web应用程序”(即MVC)模板。不要在向导中配置“身份验证”设置 - 您将在此快速入门中手动执行此操做。建立项目后,将应用程序配置为使用端口5002(有关如何执行此操做的说明,请参阅概述部分)。
要为ID链接的认证支持添加到了MVC应用程序,添加如下内容ConfigureServices
中Startup
:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.SaveTokens = true;
});
}
AddAuthentication
将身份验证服务添加到DI。做为主装置来验证用户(经过咱们使用一个cookie "Cookies"
为DefaultScheme
)。咱们设置为DefaultChallengeScheme
to,"oidc"
由于当咱们须要用户登陆时,咱们将使用OpenID Connect方案。
而后AddCookie
,咱们使用添加能够处理cookie的处理程序。
最后,AddOpenIdConnect
用于配置执行OpenID Connect协议的处理程序。这Authority
代表咱们信任IdentityServer。而后咱们经过ClientId
。识别这个客户。 SignInScheme
用于在OpenID Connect协议完成后使用cookie处理程序发出cookie。而且SaveTokens
用于在cookie中保留来自IdentityServer的令牌(由于稍后将须要它们)。
一样,咱们已经关闭了JWT声明类型映射,以容许众所周知的声明(例如“sub”和“idp”)流畅地经过:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
而后要确保认证服务执行对每一个请求,加入UseAuthentication
到Configure
中Startup
:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseAuthentication();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
应该在管道中的MVC以前添加认证中间件。
最后一步是触发身份验证握手。为此,请转到主控制器并添加[Authorize]
其中一个操做。同时修改该操做的视图以显示用户的声明,例如:
<dl>
若是您如今使用浏览器导航到该控制器,将尝试重定向到IdentityServer - 这将致使错误,由于MVC客户端还没有注册。
与OAuth 2.0相似,OpenID Connect也使用范围概念。一样,范围表明您想要保护的内容以及客户想要访问的内容。与OAuth相比,OIDC中的范围不表明API,而是表明用户ID,名称或电子邮件地址等身份数据。
经过添加新助手(in )来建立对象集合,添加对标准openid
(subject id)和profile
(名字,姓氏等)范围的支持:Config.cs``IdentityResource
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
注意
全部标准范围及其相应的声明均可以在OpenID Connect 规范中找到
而后,您须要将这些标识资源添加到IdentityServer配置中Startup.cs
。使用AddInMemoryIdentityResources
您调用的扩展方法AddIdentityServer()
:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
}
最后一步是将MVC客户端的新配置条目添加到IdentityServer。
基于OpenID Connect的客户端与咱们目前添加的OAuth 2.0客户端很是类似。但因为OIDC中的流程始终是交互式的,所以咱们须要在配置中添加一些重定向URL。
将如下内容添加到客户端配置中:
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
// other clients omitted...
// OpenID Connect implicit flow client (MVC)
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.Implicit,
// where to redirect to after login
RedirectUris = { "http://localhost:5002/signin-oidc" },
// where to redirect to after logout
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
}
}
};
}
如今终于应该为新的MVC客户端作好一切准备。
经过导航到受保护的控制器操做来触发身份验证握手。您应该会看到重定向到IdentityServer的登陆页面。
成功登陆后,将向用户显示赞成屏幕。在这里,用户能够决定是否要将他的身份信息发布到客户端应用程序。
注意
可使用RequireConsent
客户端对象上的属性基于每一个客户端关闭赞成。
..最后,浏览器重定向回客户端应用程序,显示用户的声明。
注意
在开发期间,您有时可能会看到一个异常,指出没法验证令牌。这是由于签名密钥材料是在运行中建立的,而且仅保留在内存中。当客户端和IdentityServer不一样步时会发生此异常。只需在客户端重复操做,下次元数据遇上时,一切都应该再次正常工做。
最后一步是向MVC客户端添加注销。
使用IdentityServer等身份验证服务,仅清除本地应用程序cookie是不够的。此外,您还须要向IdentityServer进行往返以清除中央单点登陆会话。
确切的协议步骤在OpenID Connect中间件中实现,只需将如下代码添加到某个控制器便可触发注销:
public async Task Logout()
{
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
}
这将清除本地cookie,而后重定向到IdentityServer。IdentityServer将清除其cookie,而后为用户提供返回MVC应用程序的连接。
如上所述,OpenID Connect中间件默认要求配置文件范围。此范围还包括名称或网站等声明。
让咱们将这些声明添加到用户,以便IdentityServer能够将它们放入身份标记:
public static List<TestUser> GetUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "password",
Claims = new []
{
new Claim("name", "Alice"),
new Claim("website", "https://alice.com")
}
},
new TestUser
{
SubjectId = "2",
Username = "bob",
Password = "password",
Claims = new []
{
new Claim("name", "Bob"),
new Claim("website", "https://bob.com")
}
}
};
}
下次进行身份验证时,您的声明页面如今会显示其余声明。
随意添加更多声明 - 以及更多范围。在Scope
对ID链接中间件属性能够在其中配置的做用域认证期间将发送到IdentityServer。
值得注意的是,对令牌声明的检索是一个可扩展性点 - IProfileService
。因为咱们正在使用AddTestUsers
,TestUserProfileService
默认使用。您能够在此处检查源代码 以查看其工做原理。
接下来,咱们将添加对外部认证的支持。这很是简单,由于您真正须要的是ASP.NET Core兼容的身份验证处理程序。
ASP.NET Core自己支持Google,Facebook,Twitter,Microsoft Account和OpenID Connect。此外,你能够找到不少其余的认证供应商实现在这里。
要使用Google进行身份验证,首先须要向他们注册。这是在他们的开发者控制台完成的。经过将/ signin-google路径添加到您的基地址(例如http:// localhost:5000 / signin-google),建立一个新项目,启用Google+ API并配置您本地IdentityServer的回调地址。
若是您在端口5000上运行 - 您只需使用下面代码段中的客户端ID / secret,由于这是咱们预先注册的。
首先将Google身份验证处理程序添加到DI。这是经过添加该代码段完成ConfigureServices
的Startup
:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// configure identity server with in-memory stores, keys, clients and scopes
services.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
下的类。
如今运行MVC客户端并尝试进行身份验证 - 您将在登陆页面上看到一个Google按钮:
身份验证后,您能够看到声明如今来自Google数据。
您能够添加其余外部提供程序。咱们有一个云托管的IdentityServer4 演示版,您可使用OpenID Connect进行集成。
将OpenId Connect处理程序添加到DI:
services.AddAuthentication()
.AddGoogle("Google", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.ClientId = "434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com";
options.ClientSecret = "3gcoTrEDPPJ0ukn_aYYT6PWo";
})
.AddOpenIdConnect("oidc", "OpenID Connect", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.Authority = "https://demo.identityserver.io/";
options.ClientId = "implicit";
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
如今,用户应该可以使用云托管的演示标识提供程序。
注意
快速入门UI自动配置外部用户。当外部用户首次登陆时,将建立新的本地用户,而且全部外部声明都将复制并与新用户关联。你处理这种状况的方式彻底取决于你。也许你想首先展现一些注册用户界面。能够在此处找到默认快速入门的源代码。能够在此处找到执行自动配置的控制器。
在以前的快速入门中,咱们探讨了API访问和用户身份验证。如今咱们想把这两个部分放在一块儿。
OpenID Connect和OAuth 2.0组合的优势在于,您可使用单个协议和使用令牌服务进行单次交换来实现这二者。
在以前的快速入门中,咱们使用了OpenID Connect隐式流程。在隐式流程中,全部令牌都经过浏览器传输,这对于身份令牌来讲是彻底正确的。如今咱们还想要一个访问令牌。
访问令牌比身份令牌更敏感,若是不须要,咱们不但愿将它们暴露给“外部”世界。OpenID Connect包含一个名为“混合流”的流程,它为咱们提供了一箭双鵰的优点,身份令牌经过浏览器渠道传输,所以客户端能够在进行任何更多工做以前对其进行验证。若是验证成功,客户端会打开令牌服务的反向通道以检索访问令牌。
没有太多必要的修改。首先,咱们但愿容许客户端使用混合流,此外咱们还但愿客户端容许执行不在用户上下文中的服务器到服务器API调用(这与咱们的客户端凭证快速启动很是类似)。这是使用该AllowedGrantTypes
属性表示的。
接下来咱们须要添加一个客户端密钥。这将用于检索反向通道上的访问令牌。
最后,咱们还让客户端访问offline_access
范围 - 这容许请求刷新令牌以实现长期存在的API访问:
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
};
MVC客户端的修改也不多 - ASP.NET Core OpenID Connect处理程序内置了对混合流的支持,所以咱们只须要更改一些配置值。
咱们配置ClientSecret
匹配IdentityServer的秘密。添加offline_access
和api1
范围,并设置ResponseType
为(这基本上意味着“使用混合流”)code id_token
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("api1");
options.Scope.Add("offline_access");
});
当您运行MVC客户端时,除了如今赞成屏幕要求您提供额外的API和脱机访问范围以外,没有太大的区别。
OpenID Connect中间件会自动为您保存令牌(在咱们的案例中为身份,访问和刷新)。这就是SaveTokens
设置的做用。
从技术上讲,令牌存储在cookie的属性部分中。访问它们的最简单方法是使用Microsoft.AspNetCore.Authentication
命名空间中的扩展方法。
例如,在您的声明视图中:
<dt>access token</dt>
<dd>@await ViewContext.HttpContext.GetTokenAsync("access_token")</dd>
<dt>refresh token</dt>
<dd>@await ViewContext.HttpContext.GetTokenAsync("refresh_token")</dd>
要使用访问令牌访问API,您须要作的就是检索令牌,并在HttpClient上设置它:
public async Task<IActionResult> CallApiUsingUserAccessToken()
{
var accessToken = await HttpContext.GetTokenAsync("access_token");
var client = new HttpClient();
client.SetBearerToken(accessToken);
var content = await client.GetStringAsync("http://localhost:5001/identity");
ViewBag.Json = JArray.Parse(content).ToString();
return View("json");
}
IdentityServer旨在提供灵活性,其中一部分容许您为用户及其数据(包括密码)使用您想要的任何数据库。若是您从一个新的用户数据库开始,那么ASP.NET Identity是您能够选择的一个选项。本快速入门展现了如何将Identity Identity与IdentityServer一块儿使用。
本快速入门假设您已经完成了全部以前的快速入门。本快速入门使用ASP.NET标识的方法是从Visual Studio中的ASP.NET标识模板建立一个新项目。这个新项目将取代咱们在以前的快速入门中从头开始构建的先前IdentityServer项目。此解决方案中的全部其余项目(针对客户端和API)将保持不变。
第一步是为您的解决方案添加ASP.NET Identity的新项目。鉴于ASP.NET Identity须要大量代码,所以使用Visual Studio中的模板是有意义的。您最终将删除IdentityServer的旧项目(假设您正在关注其余快速入门),可是您须要迁移几个项目(或者按照以前的快速入门中的描述从头开始重写)。
首先建立一个新的“ASP.NET核心Web应用程序”项目。
而后选择“Web应用程序模板(模型 - 视图 - 控制器)”选项。
而后单击“更改身份验证”按钮,并选择“我的用户账户”(这意味着使用ASP.NET身份):
最后,您的新项目对话框应该以下所示。完成后,单击“肯定”以建立项目。
不要忘记修改托管(如此处所述)以在端口5000上运行。这很重要,所以现有客户端和api项目将继续工做。
添加IdentityServer4.AspNetIdentity
NuGet包。这取决于IdentityServer4
包,所以会自动添加为传递依赖项。
尽管这是IdentityServer的新项目,但咱们仍须要与以前的快速入门相同的范围和客户端配置。将用于之前快速入门的配置类(在Config.cs中)复制到此新项目中。
必要的配置更改(暂时)是禁用MVC客户端的赞成。咱们尚未复制先前IdentityServer项目的赞成代码,因此如今对MVC客户端进行一次修改并设置RequireConsent=false
:
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = false,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
}
和之前同样,IdentityServer须要在Startup.cs中ConfigureServices
和in Configure
中配置。
ConfigureServices
这显示了为ASP.NET Identity生成的模板代码,以及IdentityServer所需的附加内容(最后)。在以前的快速入门中,AddTestUsers
扩展方法用于注册用户,但在这种状况下,咱们将该扩展方法替换AddAspNetIdentity
为使用ASP.NET Identity用户。该AddAspNetIdentity
扩展方法须要一个通用的参数,它是你的ASP.NET身份用户类型(同一个在须要AddIdentity
从模板方法)。
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add application services.
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc();
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>();
}
注意
在使用ASP.NET标识时,在DI系统中在 ASP.NET标识以后注册IdentityServer很是重要,由于IdentityServer会从ASP.NET标识覆盖某些配置。
配置
这显示了为ASP.NET Identity生成的模板代码,以及UseIdentityServer
替换调用的调用UseAuthentication
。
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
// app.UseAuthentication(); // not needed, since UseIdentityServer adds the authentication middleware
app.UseIdentityServer();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
鉴于这是一个新的ASP.NET Identity项目,您将须要建立数据库。您能够经过从项目目录运行命令提示符并运行来执行此操做,以下所示:dotnet ef database update -c ApplicationDbContext
此时,您应该可以运行项目并在数据库中建立/注册用户。启动应用程序,而后从主页单击“注册”连接:
在注册页面上建立一个新的用户账户:
如今您拥有了一个用户账户,您应该可以登陆,使用客户端并调用API。
启动MVC客户端应用程序,您应该可以单击“安全”连接以登陆。
您应该被重定向到ASP.NET Identity登陆页面。使用新建立的用户登陆:
登陆后,您应该跳过赞成页面(根据咱们上面作出的更改),并当即重定向回MVC客户端应用程序,在该应用程序中应列出您的用户声明。
您还应该可以单击“使用应用程序标识调用API”来表明用户调用API:
如今,您已使用ASP.NET Identity中的用户登陆。
IdentityServer的先前快速入门项目提供了赞成页面,错误页面和注销页面。这些缺失部分的代码能够简单地从以前的快速入门项目复制到此项目中。完成后,您最终能够删除/删除旧的IdentityServer项目。此外,一旦完成此操做,请不要忘记RequireConsent=true
在MVC客户端配置上从新启用该标志。
此快速入门的示例代码已经为您完成了这些步骤,所以您能够快速开始使用全部这些功能。请享用!
本快速入门将展现如何构建JavaScript客户端应用程序。用户将登陆IdentityServer,使用IdentityServer发出的访问令牌调用Web API,并注销IdentityServer。
为JavaScript应用程序建立一个新项目。它能够只是一个空的Web项目,也能够是一个空的ASP.NET Core应用程序。此快速入门将使用空的ASP.NET Core应用程序。
建立一个新的ASP.NET Core Web应用程序:
选择“空”模板:
单击“肯定”按钮以建立项目。
修改托管(如此处所述)以在端口5003上运行。
鉴于该项目主要用于客户端,咱们须要ASP.NET Core来提供构成咱们应用程序的静态HTML和JavaScript文件。静态文件中间件旨在实现此目的。
在方法中注册Startup.cs中的静态文件中间件Configure
:
public void Configure(IApplicationBuilder app)
{
app.UseDefaultFiles();
app.UseStaticFiles();
}
此中间件如今将从应用程序的〜/ wwwroot文件夹中提供静态文件。这是咱们将放置HTML和JavaScript文件的地方。
在MVC项目中,咱们使用库来处理OpenID Connect协议。在这个项目中,咱们须要一个相似的库,除了一个在JavaScript中运行而且设计为在浏览器中运行的库。该OIDC客户端库就是这样一个图书馆。它能够经过NPM,Bower以及从github 直接下载。
NPM
若是要使用NPM下载oidc-client,请按照如下步骤操做:
将新的NPM包文件添加到项目中并将其命名为package.json:
在的package.json一个补充dependency
到oidc-client
:
"dependencies": {
"oidc-client": "1.4.1"
}
保存此文件后,Visual Studio应自动将这些包还原到名为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
这将是咱们的应用程序中的主页。它将只包含用于登陆,注销和调用Web API的按钮的HTML。它还将包含<script>
标记以包含咱们的两个JavaScript文件。它还包含<pre>
用于向用户显示消息的用途。
它应该以下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<button id="login">Login</button>
<button id="api">Call API</button>
<button id="logout">Logout</button>
<pre id="results"></pre>
<script src="oidc-client.js"></script>
<script src="app.js"></script>
</body>
</html>
app.js
这将包含咱们的应用程序的主要代码。第一件事是添加一个帮助函数来将消息记录到<pre>
:
function log() {
document.getElementById('results').innerText = '';
Array.prototype.forEach.call(arguments, function (msg) {
if (msg instanceof Error) {
msg = "Error: " + msg.message;
}
else if (typeof msg !== '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链接协议。它须要MVC Client中必需的相似配置(尽管具备不一样的值)。添加此代码以配置和实例化UserManager
:
var config = {
authority: "http://localhost:5000",
client_id: "js",
redirect_uri: "http://localhost:5003/callback.html",
response_type: "id_token token",
scope:"openid profile api1",
post_logout_redirect_uri : "http://localhost:5003/index.html",
};
var mgr = new Oidc.UserManager(config);
接下来,UserManager
提供getUser
API以了解用户是否登陆到JavaScript应用程序。它使用JavaScript Promise
以异步方式返回结果。返回的User
对象具备profile
包含用户声明的属性。添加此代码以检测用户是否已登陆JavaScript应用程序:
mgr.getUser().then(function (user) {
if (user) {
log("User logged in", user.profile);
}
else {
log("User not logged in");
}
});
接下来,咱们要实现的login
,api
和logout
功能。在UserManager
提供了signinRedirect
登陆用户,而且signoutRedirect
以注销用户。User
咱们在上面的代码中得到的对象还具备access_token
可用于经过Web API进行身份验证的属性。在access_token
将被传递给经过网络API 受权与头承载方案。添加此代码以在咱们的应用程序中实现这三个功能:
function login() {
mgr.signinRedirect();
}
function api() {
mgr.getUser().then(function (user) {
var url = "http://localhost:5001/identity";
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = function () {
log(xhr.status, JSON.parse(xhr.responseText));
}
xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
xhr.send();
});
}
function logout() {
mgr.signoutRedirect();
}
callback.html
redirect_uri
一旦用户登陆IdentityServer,此HTML文件就是指定的页面。它将完成与IdentityServer的OpenID Connect协议登陆握手。这个代码所有由UserManager
咱们以前使用的类提供。登陆完成后,咱们能够将用户重定向回主index.html页面。添加此代码以完成登陆过程:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<script src="oidc-client.js"></script>
<script>
new Oidc.UserManager().signinRedirectCallback().then(function () {
window.location = "index.html";
}).catch(function (e) {
console.error(e);
});
</script>
</body>
</html>
既然客户端应用程序已经准备就绪,咱们须要在IdentityServer中为这个新的JavaScript客户端定义一个配置条目。在IdentityServer项目中找到客户端配置(在Config.cs中)。将新客户端添加到咱们的新JavaScript应用程序的列表中。它应具备下面列出的配置:
// JavaScript Client
new Client
{
ClientId = "js",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:5003/callback.html" },
PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
AllowedCorsOrigins = { "http://localhost:5003" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
}
}
最后一点配置是在Web API项目中配置CORS。这将容许从http:// localhost:5003到http:// localhost:5001进行Ajax调用。
配置CORS
ConfigureServices
在Startup.cs中将CORS服务添加到依赖注入系统:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "api1";
});
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.WithOrigins("http://localhost:5003")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
}
将CORS中间件添加到管道中Configure
:
public void Configure(IApplicationBuilder app)
{
app.UseCors("default");
app.UseAuthentication();
app.UseMvc();
}
如今您应该可以运行JavaScript客户端应用程序:
单击“登陆”按钮以对用户进行签名。一旦用户返回到JavaScript应用程序,您应该看到他们的我的资料信息:
而后单击“API”按钮以调用Web API:
最后点击“退出”以签署用户。
您如今能够开始使用IdentityServer进行登陆,注销和验证对Web API的调用的JavaScript客户端应用程序。
IdentityServer旨在实现可扩展性,其中一个可扩展点是用于IdentityServer所需数据的存储机制。本快速入门展现了如何配置IdentityServer以使用EntityFramework(EF)做为此数据的存储机制(而不是使用咱们迄今为止使用的内存中实现)。
注意
除了手动配置EF支持外,还有一个IdentityServer模板可用于建立具备EF支持的新项目。使用建立它。有关更多信息,请参见此处dotnet new is4ef
咱们正在向数据库移动两种类型的数据。第一个是配置数据(资源和客户端)。第二个是IdentityServer在使用时产生的操做数据(令牌,代码和赞成)。这些存储使用接口建模,咱们在IdentityServer4.EntityFramework Nuget包中提供这些接口的EF实现。
经过添加IdentityServer项目的IdentityServer4.EntityFramework Nuget包的引用开始。
鉴于EF的灵活性,您可使用任何EF支持的数据库。对于本快速入门,咱们将使用Visual Studio附带的SqlServer的LocalDb版本。
该IdentityServer4.EntityFramework包中包含从IdentityServer的模型映射实体类。做为IdentityServer的车型变化,因此会在实体类IdentityServer4.EntityFramework。当您使用IdentityServer4.EntityFramework并随着时间的推移升级时,您将负责本身的数据库架构以及实体类更改时该架构所需的更改。管理这些更改的一种方法是使用EF迁移,此快速入门将显示如何完成此操做。若是迁移不是您的首选项,那么您能够以任何您认为合适的方式管理架构更改。
注意
为IdentityServer4.EntityFramework中的实体维护SqlServer的SQL脚本。他们就在这里。
除了使用EF迁移跟踪架构更改以外,咱们还将使用它在数据库中建立初始架构。这须要使用EF Core工具(此处有更多详细信息)。咱们如今将添加它们,不幸的是,这必须经过手动编辑.csproj文件来完成。要经过右键单击项目来编辑.csproj,而后选择“编辑projectname.csproj”:
注意
根据您为IdentityServer主机建立初始项目的方式,您可能已在csproj文件中配置了这些工具。若是是,您能够跳到下一部分。
而后在结尾</ Project>元素以前添加如下代码段:
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
</ItemGroup>
它应该看起来像这样:
保存并关闭文件。要测试您是否正确安装了这些工具,能够在与项目相同的目录中打开命令shell并运行dotnet ef。它应该以下所示:
接下来的步骤是,以取代当前呼叫AddInMemoryClients
,AddInMemoryIdentityResources
和AddInMemoryApiResources
在ConfigureServices
在方法Startup.cs。咱们将使用如下代码替换它们:
const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer4.Quickstart.EntityFramework-2.0.0;trusted_connection=yes;";
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(Config.GetUsers())
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30;
});
您可能须要将这些命名空间添加到文件中:
using Microsoft.EntityFrameworkCore;
using System.Reflection;
上面的代码是对链接字符串进行硬编码,若是您愿意,能够随意更改。此外,调用AddConfigurationStore
和AddOperationalStore
注册EF支持的商店实现。
传递给这些API的“构建器”回调函数是EF机制,容许您为这两个存储中的每个配置DbContextOptionsBuilder
for DbContext
。这就是咱们的DbContext
类可使用您要使用的数据库提供程序进行配置的方式。在这种状况下,经过调用UseSqlServer
咱们正在使用SqlServer。您也能够看出,这是提供链接字符串的位置。
“options”回调函数用于UseSqlServer
配置定义EF迁移的程序集。EF须要使用迁移来定义数据库的模式。
注意
托管应用程序负责定义这些迁移,由于它们特定于您的数据库和提供程序。
咱们接下来会添加迁移。
要建立迁移,请在IdentityServer项目目录中打开命令提示符。在命令提示符下运行如下两个命令:
dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb
它应该看起来像这样:
您如今应该在项目中看到〜/ Data / Migrations / IdentityServer文件夹。其中包含新建立的迁移的代码。
注意
若是您的数据库项目是一个单独的类库,并修复了错误“没法建立类型的对象”<您的名字> DbContext'。将“IDesignTimeDbContextFactory”的实现添加到项目中,或者参阅https://go.microsoft.com/fwlink/?linkid=851728以获取在设计时支持的其余模式。经过添加IDesignTimeDbContextFactory的实现,您还须要PersistedGrantDbContext和ConfigurationDbContext的工厂实现。
如今咱们已经进行了迁移,咱们能够编写代码来从迁移中建立数据库。咱们还将使用咱们在以前的快速入门中定义的内存配置数据来为数据库设定种子。
在Startup.cs中添加此方法以帮助初始化数据库:
private void InitializeDatabase(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
context.Database.Migrate();
if (!context.Clients.Any())
{
foreach (var client in Config.GetClients())
{
context.Clients.Add(client.ToEntity());
}
context.SaveChanges();
}
if (!context.IdentityResources.Any())
{
foreach (var resource in Config.GetIdentityResources())
{
context.IdentityResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
if (!context.ApiResources.Any())
{
foreach (var resource in Config.GetApiResources())
{
context.ApiResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
}
}
而后咱们能够从Configure
方法中调用它:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// this will do the initial DB population
InitializeDatabase(app);
// the rest of the code that was already here
// ...
}
如今,若是运行IdentityServer项目,则应建立数据库并使用快速入门配置数据进行种子设定。您应该可以使用SQL Server Management Studio或Visual Studio来链接和检查数据。
注意
上面的InitializeDatabase
辅助API能够方便地为数据库设定种子,可是这种方法并不适合每次运行应用程序时执行。填充数据库后,请考虑删除对API的调用。
您如今应该可以运行任何现有的客户端应用程序并登陆,获取令牌并调用API - 全部这些都基于数据库配置。
注意
本节中的代码仍然依赖于Config.cs及其虚构用户Alice和Bob。若是您的用户列表很简短且静态,则调整后的Config.cs版本可能就足够了,但您可能但愿在数据库中动态管理更大且更流畅的用户列表。ASP.NET Identity是一个须要考虑的选项,下一节的快速入门列出了此解决方案的示例实现。
IdentityServer组织不维护这些示例。IdentityServer组织愉快地连接到社区样本,但不能对样本作出任何保证。请直接与做者联系。
https://github.com/leastprivilege/AspNetCoreSecuritySamples
此示例结合了EF和ASP.NET Identity快速入门(#6和#8)。
此示例显示如何在与保护API的IdentityServer相同的主机中托管API。
https://github.com/brockallen/IdentityServerAndApi
IdentityServer4-mongo:与Quickstart#8 EntityFramework配置相似,但使用MongoDB配置数据。
IdentityServer4-mongo-AspIdentity:更详细的示例基于使用ASP.NET Identity进行身份管理,使用MongoDB做为配置数据
https://github.com/souzartn/IdentityServer4.Samples.Mongo
显示如何使用扩展受权将外部身份验证令牌交换到身份服务器访问令牌
https://github.com/waqaskhan540/IdentityServerExternalAuth
基于Razor Pages的QuickStart示例由Martin Fletcher提供。
显示可信“内部”应用程序和“外部”应用程序与.NET Core 2.0和ASP.NET Core 2.0应用程序的交互
https://github.com/BenjaminAbt/Samples.AspNetCore-IdentityServer4
演示如何使用IdentityServer4中的JWKS端点和RS256算法保护节点(Express)API。
使用更高质量的生产就绪模块提供IdentityServer4.Samples中NodeJsApi样本的替代方案。
https://github.com/lyphtec/idsvr4-node-jwks
IdentityServer是中间件和服务的组合。全部配置都在您的启动类中完成。
您能够经过调用如下方法将IdentityServer服务添加到DI系统:
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer();
}
您能够选择将选项传入此调用。有关选项的详细信息,请参见此
这将返回一个构建器对象,该构建器对象又有许多方便的方法来链接其余服务。
AddSigningCredential
添加签名密钥服务,该服务为各类令牌建立/验证服务提供指定的密钥材料。您能够从证书存储中传入证书的a X509Certificate2
,a SigningCredential
或引用。
AddDeveloperSigningCredential
在启动时建立临时密钥材料。当您没有要使用的证书时,这仅适用于dev。生成的密钥将保留到文件系统,以便在服务器从新启动之间保持稳定(能够经过传递禁用false
)。这解决了客户端/ api元数据缓存在开发期间不一样步时的问题。
AddValidationKey
添加用于验证令牌的密钥。它们将由内部令牌验证器使用,并将显示在发现文档中。您能够从证书存储中传入证书的a X509Certificate2
,a SigningCredential
或引用。这对于关键翻转场景很是有用。
各类“内存中”配置API容许从内存中的配置对象列表配置IdentityServer。这些“内存中”集合能够在宿主应用程序中进行硬编码,也能够从配置文件或数据库动态加载。可是,经过设计,这些集合仅在托管应用程序启动时建立。
使用这些配置API的目的是在原型设计,开发和/或测试时使用,在这种状况下,不须要在运行时为配置数据动态查询数据库。若是配置不多更改,则此配置样式也可能适用于生产方案,或者若是必须更改值,则要求从新启动应用程序并不方便。
AddInMemoryClients
基于配置对象的内存中集合的注册IClientStore
和ICorsPolicyService
实现Client
。
AddInMemoryIdentityResources
IResourceStore
根据IdentityResource
配置对象的内存中集合注册实现。
AddInMemoryApiResources
IResourceStore
根据ApiResource
配置对象的内存中集合注册实现。
该TestUser
级车型的用户,他们的凭据,并在IdentityServer索赔。使用TestUser
“内存”商店是由于它适用于原型设计,开发和/或测试。采用TestUser
在生产中不推荐使用。
AddTestUsers
TestUserStore
基于TestUser
对象集合的注册。 TestUserStore
由默认的快速入门UI使用。还注册IProfileService
和的实现IResourceOwnerPasswordValidator
。
AddExtensionGrantValidator
添加IExtensionGrantValidator
实现以用于扩展受权。
AddSecretParser
添加ISecretParser
用于解析客户端或API资源凭据的实现。
AddSecretValidator
添加ISecretValidator
用于针对凭证存储验证客户端或API资源凭证的实现。
AddResourceOwnerValidator
添加IResourceOwnerPasswordValidator
用于验证资源全部者密码凭据授予类型的用户凭据的实现。
AddProfileService
添加IProfileService
用于链接到自定义用户配置文件存储的实现。的DefaultProfileService
类提供了依赖于验证cookie做为权利要求中的用于在令牌发行的惟一来源的默认实现。
AddAuthorizeInteractionResponseGenerator
添加IAuthorizeInteractionResponseGenerator
实现以在受权端点处定制逻辑,以便在必须向用户显示错误,登陆,赞成或任何其余自定义页面的UI时。本AuthorizeInteractionResponseGenerator
类提供了一个默认的实现,所以考虑从这个现有的类派生若是须要加强现有的行为。
AddCustomAuthorizeRequestValidator
添加ICustomAuthorizeRequestValidator
实现以在受权端点处自定义请求参数验证。
AddCustomTokenRequestValidator
添加ICustomTokenRequestValidator
实现以在令牌端点处自定义请求参数验证。
AddRedirectUriValidator
添加IRedirectUriValidator
实现以自定义重定向URI验证。
AddAppAuthRedirectUriValidator
添加符合重定向URI验证器的“AppAuth”(适用于Native Apps的OAuth 2.0)(进行严格验证,但也容许带有随机端口的http://127.0.0.1)。
AddJwtBearerClientAuthentication
使用JWT承载断言添加对客户端身份验证的支持。
IdentityServer常用客户端和资源配置数据。若是从数据库或其余外部存储加载此数据,则频繁从新加载相同数据可能会很昂贵。
AddInMemoryCaching
要使用下面描述的任何缓存,ICache<T>
必须在DI中注册实现。此API注册了ICache<T>
基于ASP.NET Core的默认内存实现MemoryCache
。
AddClientStoreCache
注册IClientStore
装饰器实现,该实现将维护Client
配置对象的内存缓存。缓存持续时间可在Caching
配置选项上配置IdentityServerOptions
。
AddResourceStoreCache
注册一个IResourceStore
装饰器实现,它将维护内存缓存IdentityResource
和ApiResource
配置对象。缓存持续时间可在Caching
配置选项上配置IdentityServerOptions
。
AddCorsPolicyCache
注册ICorsPolicyService
装饰器实现,该实现将维护CORS策略服务评估结果的内存缓存。缓存持续时间可在Caching
配置选项上配置IdentityServerOptions
。
能够进一步自定义缓存:
默认缓存依赖于ICache<T>
实现。若是要自定义特定配置对象的缓存行为,能够在依赖项注入系统中替换此实现。
ICache<T>
自己的默认实现依赖于.NET提供的IMemoryCache
接口(和MemoryCache
实现)。若是要自定义内存中缓存行为,能够替换IMemoryCache
依赖项注入系统中的实现。
您须要经过调用如下方法将IdentityServer添加到管道:
public void Configure(IApplicationBuilder app)
{
app.UseIdentityServer();
}
注意
UseIdentityServer
包括打电话UseAuthentication
,所以没有必要同时使用。
中间件没有其余配置。
请注意,订单在管道中很重要。例如,您须要在实现登陆屏幕的UI框架以前添加IdentitySever。
您一般在系统中定义的第一件事是您要保护的资源。这多是您的用户的身份信息,如我的资料数据或电子邮件地址,或访问API。
注意
您可使用C#对象模型定义资源 - 或从数据存储加载它们。的实施IResourceStore
与这些低级别的细节交易。对于本文档,咱们使用内存中实现。
身份资源是用户的用户ID,名称或电子邮件地址等数据。标识资源具备惟一名称,您能够为其分配任意声明类型。而后,这些声明将包含在用户的身份令牌中。客户端将使用该scope
参数来请求访问标识资源。
OpenID Connect规范指定了几个标准身份资源。最低要求是,您为用户发送惟一ID提供支持 - 也称为主题ID。这是经过公开名为的标准身份资源来完成的openid
:
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId()
};
}
该IdentityResources类支持的规范(OpenID的,电子邮件,我的资料,电话和地址)中定义的全部范围。若是您想所有支持它们,能够将它们添加到支持的身份资源列表中:
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Email(),
new IdentityResources.Profile(),
new IdentityResources.Phone(),
new IdentityResources.Address()
};
}
您还能够定义自定义标识资源。建立一个新的IdentityResource类,为其命名,并可选择显示名称和描述,并定义在请求此资源时应将哪些用户声明包含在身份令牌中:
public static IEnumerable<IdentityResource> GetIdentityResources()
{
var customProfile = new IdentityResource(
name: "custom.profile",
displayName: "Custom profile",
claimTypes: new[] { "name", "email", "status" });
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
customProfile
};
}
有关身份资源设置的更多信息,请参阅参考部分。
要容许客户端请求API的访问令牌,您须要定义API资源,例如:
要获取API的访问权限,您还须要将它们注册为范围。此次范围类型是Resource类型:
public static IEnumerable<ApiResource> GetApis()
{
return new[]
{
// simple API with a single scope (in this case the scope name is the same as the api name)
new ApiResource("api1", "Some API 1"),
// expanded version if more control is needed
new ApiResource
{
Name = "api2",
// secret for using introspection endpoint
ApiSecrets =
{
new Secret("secret".Sha256())
},
// include the following using claims in access token (in addition to subject id)
UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.Email },
// this API defines two scopes
Scopes =
{
new Scope()
{
Name = "api2.full_access",
DisplayName = "Full access to API 2",
},
new Scope
{
Name = "api2.read_only",
DisplayName = "Read only access to API 2"
}
}
}
};
}
有关API资源设置的更多信息,请参阅参考部分。
注意
由资源定义的用户声明由IProfileService扩展点加载。
客户端表示能够从您的身份服务器请求令牌的应用程序。
详细信息各不相同,但您一般会为客户端定义如下经常使用设置:
惟一的客户ID
若是须要的秘密
容许与令牌服务的交互(称为受权类型)
发送身份和/或访问令牌的网络位置(称为重定向URI)
容许客户端访问的范围列表(也称为资源)
注意
在运行时,经过实现来检索客户端IClientStore
。这容许从任意数据源(如配置文件或数据库)加载它们。对于本文档,咱们将使用客户端存储的内存版本。您能够ConfigureServices
经过AddInMemoryClients
extensions方法链接内存存储。
在这种状况下,没有交互式用户 - 服务(也称为客户端)想要与API(aka范围)进行通讯:
public class Clients
{
public static IEnumerable<Client> Get()
{
return new List<Client>
{
new Client
{
ClientId = "service.client",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes = { "api1", "api2.read_only" }
}
};
}
}
此客户端使用所谓的隐式流来从JavaScript请求身份和访问令牌:
var jsClient = new Client
{
ClientId = "js",
ClientName = "JavaScript Client",
ClientUri = "http://identityserver.io",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:7017/index.html" },
PostLogoutRedirectUris = { "http://localhost:7017/index.html" },
AllowedCorsOrigins = { "http://localhost:7017" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"api1", "api2.read_only"
}
};
交互式服务器端(或本机桌面/移动)应用程序使用混合流。此流程为您提供最佳安全性,由于访问令牌仅经过反向通道调用传输(并容许您访问刷新令牌):
var mvcClient = new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
ClientUri = "http://identityserver.io",
AllowedGrantTypes = GrantTypes.Hybrid,
AllowOfflineAccess = true,
ClientSecrets = { new Secret("secret".Sha256()) },
RedirectUris = { "http://localhost:21402/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:21402/" },
FrontChannelLogoutUri = "http://localhost:21402/signout-oidc",
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"api1", "api2.read_only"
},
};
为了使IdentityServer可以表明用户发出令牌,该用户必须登陆IdentityServer。
使用由ASP.NET Core中的cookie身份验证处理程序管理的cookie来跟踪身份验证。
IdentityServer注册了两个cookie处理程序(一个用于身份验证会话,另外一个用于临时外部cookie)。默认状况下使用它们,若是要手动引用它们,能够从IdentityServerConstants
类(DefaultCookieAuthenticationScheme
和ExternalCookieAuthenticationScheme
)中获取它们的名称。
咱们只公开这些cookie的基本设置(到期和滑动),若是您须要更多控制,您能够注册本身的cookie处理程序。IdentityServer使用与使用ASP.NET Core 时DefaultAuthenticateScheme
配置的cookie处理程序相匹配的cookie处理程序。AuthenticationOptions``AddAuthentication
若是您但愿使用本身的cookie身份验证处理程序,则必须本身配置它。这必须ConfigureServices
在DI(with AddIdentityServer
)中注册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不为用户身份验证提供任何用户界面或用户数据库。这些是您但愿本身提供或发展的东西。
若是您须要基本UI的起点(登陆,注销,赞成和管理受权),您可使用咱们的快速入门UI。
快速入门UI针对内存数据库对用户进行身份验证。您能够经过访问真实用户存储来替换这些位。咱们有使用ASP.NET Identity的示例。
当IdentityServer在受权端点收到请求且未对用户进行身份验证时,将将用户重定向到已配置的登陆页面。您必须经过选项上的UserInteraction
设置(默认为)通知IdentityServer登陆页面的路径。将传递一个参数,通知您的登陆页面,登陆完成后应重定向用户。/account/login``returnUrl
注意
经过参数注意开放重定向攻击returnUrl
。您应该验证returnUrl
引用的是众所周知的位置。请参阅API 的交互服务以验证returnUrl
参数。
在您的登陆页面上,您可能须要有关请求上下文的信息,以便自定义登陆体验(例如客户端,提示参数,IdP提示或其余内容)。这能够经过交互服务GetAuthorizationContextAsync
上的API得到。
在HttpContext
ASP.NET Core 上有与身份验证相关的扩展方法,用于发出身份验证cookie并对用户进行签名。使用的身份验证方案必须与您正在使用的cookie处理程序匹配(请参见上文)。
当您签署用户时,您必须至少发出sub
索赔和name
索赔。IdentityServer还提供了一些SignInAsync
扩展方法HttpContext
,使其更加方便。
您还能够选择发出idp
声明(针对身份提供者名称),amr
声明(针对所使用的身份验证方法)和/或auth_time
声明(针对用户身份验证的纪元时间)。若是您不提供这些,则IdentityServer将提供默认值。
ASP.NET Core有一种灵活的方式来处理外部身份验证。这涉及几个步骤。
注意
若是您使用的是ASP.NET标识,则会隐藏许多基础技术细节。建议您还阅读Microsoft 文档并执行ASP.NET Identity 快速入门。
与外部提供者通讯所需的协议实现封装在身份验证处理程序中。一些提供商使用专有协议(例如Facebook等社交提供商),有些提供商使用标准协议,例如OpenID Connect,WS-Federation或SAML2p。
有关添加外部身份验证和配置它的分步说明,请参阅此快速入门。
调用外部身份验证处理程序的一个选项SignInScheme
,例如:
services.AddAuthentication()
.AddGoogle("Google", options =>
{
options.SignInScheme = "scheme of cookie handler to use";
options.ClientId = "...";
options.ClientSecret = "...";
})
登陆方案指定将临时存储外部认证结果的cookie处理程序的名称,例如由外部提供者发送的声明。这是必要的,由于在完成外部身份验证过程以前一般会涉及一些重定向。
鉴于这是一种常见作法,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 = "...";
})
注意
对于特殊状况,您还能够将外部cookie机制短路并将外部用户直接转发到主cookie处理程序。这一般涉及处理外部处理程序上的事件,以确保您从外部标识源执行正确的声明转换。
您能够经过(或使用MVC )ChallengeAsync
上的扩展方法调用外部认证处理程序。HttpContext``ChallengeResult
您一般但愿将一些选项传递给挑战操做,例如回调页面的路径和簿记提供者的名称,例如:
var callbackUrl = Url.Action("ExternalLoginCallback");
var props = new AuthenticationProperties
{
RedirectUri = callbackUrl,
Items =
{
{ "scheme", provider },
{ "returnUrl", returnUrl }
}
};
return Challenge(provider, props);
在回调页面上,您的典型任务是:
检查外部提供商返回的身份。
决定如何处理该用户。若是这是新用户或返回用户,则可能会有所不一样。
新用户在被容许以前可能须要额外的步骤和UI。
可能会建立一个连接到外部提供程序的新内部用户账户。
存储您要保留的外部声明。
删除临时cookie
登陆用户
检查外部身份:
// read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
// retrieve claims of the external user
var externalUser = result.Principal;
if (externalUser == null)
{
throw new Exception("External authentication error");
}
// retrieve claims of the external user
var claims = externalUser.Claims.ToList();
// try to determine the unique id of the external user - the most common claim type for that are the sub claim and the NameIdentifier
// depending on the external provider, some other claim type might be used
var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject);
if (userIdClaim == null)
{
userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
}
if (userIdClaim == null)
{
throw new Exception("Unknown userid");
}
var externalUserId = userIdClaim.Value;
var externalProvider = userIdClaim.Issuer;
// use externalProvider and externalUserId to find your user, or provision a new user
清理和登陆:
// issue authentication cookie for user
await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, props, additionalClaims.ToArray());
// delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
// validate return URL and redirect back to authorization endpoint or a local page
if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return Redirect("~/");
重定向到外部提供程序以进行登陆时,必须常常从客户端应用程序进行往返状态。这意味着在离开客户端以前捕获状态并保留状态,直到用户返回到客户端应用程序。许多协议(包括OpenID Connect)容许将某种状态做为参数传递做为请求的一部分,而且身份提供者将在响应上返回该状态。ASP.NET Core提供的OpenID Connect身份验证处理程序利用了协议的这一功能,这就是它实现上述returnUrl
功能的方式。
在请求参数中存储状态的问题是请求URL可能变得太大(超过2000个字符的公共限制)。OpenID Connect身份验证处理程序确实提供了一个可扩展点,用于在服务器中而不是在请求URL中存储状态。您能够经过ISecureDataFormat<AuthenticationProperties>
在OpenIdConnectOptions上实现和配置它来自行实现。
幸运的是,IdentityServer为您提供了一个实现,由IDistributedCache
DI容器中注册的实现(例如标准MemoryDistributedCache
)支持。要使用IdentityServer提供的安全数据格式实现,只需在配置DI时调用AddOidcStateDataFormatterCache
扩展方法IServiceCollection
。若是未传递任何参数,则配置的全部OpenID Connect处理程序将使用IdentityServer提供的安全数据格式实现:
public void ConfigureServices(IServiceCollection services)
{
// configures the OpenIdConnect handlers to persist the state parameter into the server-side IDistributedCache.
services.AddOidcStateDataFormatterCache();
services.AddAuthentication()
.AddOpenIdConnect("demoidsrv", "IdentityServer", options =>
{
// ...
})
.AddOpenIdConnect("aad", "Azure AD", options =>
{
// ...
})
.AddOpenIdConnect("adfs", "ADFS", options =>
{
// ...
});
}
若是只配置特定方案,则将这些方案做为参数传递:
public void ConfigureServices(IServiceCollection services)
{
// configures the OpenIdConnect handlers to persist the state parameter into the server-side IDistributedCache.
services.AddOidcStateDataFormatterCache("aad", "demoidsrv");
services.AddAuthentication()
.AddOpenIdConnect("demoidsrv", "IdentityServer", options =>
{
// ...
})
.AddOpenIdConnect("aad", "Azure AD", options =>
{
// ...
})
.AddOpenIdConnect("adfs", "ADFS", options =>
{
// ...
});
}
在支持的平台上,您可使用IdentityServer对使用Windows身份验证的用户进行身份验证(例如,针对Active Directory)。当前使用如下命令托管IdentityServer时,Windows身份验证可用:
在这两种状况下,使用该方案的ChallengeAsync
API 都会触发Windows身份验证。咱们的快速入门UI中的账户控制器实现了必要的逻辑。HttpContext``"Windows"
使用Kestrel时,必须运行“后面”IIS并使用IIS集成:
var host = new WebHostBuilder()
.UseKestrel()
.UseUrls("http://localhost:5000")
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
使用该WebHost.CreateDefaultBuilder
方法设置时,会自动配置红隼WebHostBuilder
。
IIS(或IIS Express)中的虚拟目录也必须启用Windows并启用匿名身份验证。
IIS集成层将Windows身份验证处理程序配置为DI,能够经过身份验证服务调用。一般在IdentityServer中,建议禁用此自动行为。这是在ConfigureServices
:
services.Configure<IISOptions>(iis =>
{
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
});
注意
默认状况下,显示名称为空,Windows身份验证按钮不会显示在快速入门UI中。若是依赖于自动发现外部提供程序,则须要设置显示名称。
注销IdentityServer就像删除身份验证cookie同样简单,可是为了完成联合注销,咱们必须考虑将用户从客户端应用程序(甚至多是上游身份提供商)中签名。
要删除身份验证cookie,只需使用SignOutAsync
扩展方法便可HttpContext
。您将须要传递使用的方案(IdentityServerConstants.DefaultCookieAuthenticationScheme
除非您已更改,不然提供此方案):
await HttpContext.SignOutAsync(IdentityServerConstants.DefaultCookieAuthenticationScheme);
或者您可使用IdentityServer提供的便捷扩展方法:
await HttpContext.SignOutAsync();
注意
一般,您应该提示用户注销(意味着须要POST),不然攻击者可能会连接到您的注销页面,致使用户自动注销。
做为退出流程的一部分,您须要确保客户端应用程序被告知用户已退出。IdentityServer支持服务器端客户端的前端通道规范(例如MVC),服务器端客户端的反向通道 规范(例如MVC),以及基于浏览器的JavaScript客户端的会话管理规范(例如SPA,React,Angular)等)。
前端服务器端客户端
要经过前端通道规范从服务器端客户端应用程序注销用户,IdentityServer中的“已注销”页面必须呈现<iframe>
以通知客户端用户已注销。但愿收到通知的客户端必须FrontChannelLogoutUri
设置配置值。IdentityServer跟踪用户已登陆的客户端,并提供GetLogoutContextAsync
在IIdentityServerInteractionService
(详细信息)上调用的API 。此API返回一个LogoutRequest
对象,该对象具备SignOutIFrameUrl
您已注销的页面必须呈现为的属性<iframe>
。
反向通道服务器端客户端
要经过反向通道规范从服务器端客户端应用程序注销用户SignOutIFrameUrl
,IdentityServer中的端点将自动触发服务器到服务器调用,将签名的注销请求传递给客户端。这意味着即便若是没有前面通道的客户端中,“退出”,在IdentityServer页仍必须渲染<iframe>
到SignOutIFrameUrl
如上所述。但愿收到通知的客户端必须BackChannelLogoutUri
设置配置值。
基于浏览器的JavaScript客户端
鉴于会话管理规范的设计方式,IdentityServer中没有什么特别的,您须要作的是通知这些客户端用户已注销。可是,客户端必须对check_session_iframe执行监视,这是由oidc-client JavaScript库实现的。
若是客户端应用程序启动了注销,则客户端首先将用户重定向到结束会话端点。在结束会话端点处的处理可能须要经过重定向到注销页面来维护一些临时状态(例如,客户端的注销后重定向uri)。此状态可能对注销页面有用,而且状态的标识符经过logoutId参数传递到注销页面。
在GetLogoutContextAsync
上的API 交互服务能够用来加载状态。感兴趣的ShowSignoutPrompt
是ShowSignoutPrompt
指示注销请求是否已通过身份验证,所以不会提示用户注销是安全的。
默认状况下,此状态做为经过logoutId值传递的受保护数据结构进行管理。若是您但愿在结束会话端点和注销页面之间使用其余一些持久性,那么您能够IMessageStore<LogoutMessage>
在DI中实现并注册实现。
当用户注销 IdentityServer,而且他们使用外部身份提供程序登陆时,可能会将其重定向到也注销外部提供程序。并不是全部外部提供商都支持注销,由于它取决于它们支持的协议和功能。
要检测是否必须将用户重定向到外部身份提供程序以进行注销一般是经过使用idp
在IdentityServer中发布到cookie中的声明来完成的。设置到此声明中的值是AuthenticationScheme
相应的身份验证中间件。在签出时,咨询此索赔以了解是否须要外部签出。
因为正常注销工做流程已经须要清理和状态管理,所以将用户重定向到外部身份提供商是有问题的。而后,在IdentityServer完成正常注销和清理过程的惟一方法是从外部身份提供程序请求在注销后将用户重定向回IdentityServer。并不是全部外部提供商都支持退出后重定向,由于它取决于它们支持的协议和功能。
而后,签出时的工做流程将撤消IdentityServer的身份验证cookie,而后重定向到请求退出后重定向的外部提供程序。退出后重定向应保持此处描述的必要签出状态(即logoutId
参数值)。要在外部提供程序注销后重定向回IdentityServer,RedirectUri
应该AuthenticationProperties
在使用ASP.NET Core的SignOutAsync
API 时使用,例如:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutInputModel model)
{
// build a model so the logged out page knows what to display
var vm = await _account.BuildLoggedOutViewModelAsync(model.LogoutId);
var user = HttpContext.User;
if (user?.Identity.IsAuthenticated == true)
{
// delete local authentication cookie
await HttpContext.SignOutAsync();
// raise the logout event
await _events.RaiseAsync(new UserLogoutSuccessEvent(user.GetSubjectId(), user.GetName()));
}
// check if we need to trigger sign-out at an upstream identity provider
if (vm.TriggerExternalSignout)
{
// build a return URL so the upstream provider will redirect back
// to us after the user has logged out. this allows us to then
// complete our single sign-out processing.
string url = Url.Action("Logout", new { logoutId = vm.LogoutId });
// this triggers a redirect to the external provider for sign-out
return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme);
}
return View("LoggedOut", vm);
}
一旦用户退出外部提供程序而后重定向回来,IdentityServer的正常注销处理应该执行,这涉及处理logoutId
和执行全部必要的清理。
联合注销是指用户使用外部身份提供程序登陆IdentityServer,而后用户经过IdentityServer未知的工做流程注销该外部身份提供程序的状况。当用户注销时,对IdentityServer进行通知将很是有用,这样它就能够将用户从IdentityServer和使用IdentityServer的全部应用程序签名。
并不是全部外部身份提供商都支持联合注销,但那些提供的机制将提供通知客户端用户已注销的机制。此通知一般以<iframe>
来自外部身份提供商的“已注销”页面的请求的形式出现。而后IdentityServer必须通知其全部客户(如讨论这里),也一般在一个请求的形式<iframe>
从内外部身份提供的<iframe>
。
使联合注销成为特殊状况(与正常注销相比)的缘由是联合注销请求不是IdentityServer中的正常注销端点。实际上,每一个外部IdentityProvider都将在IdentityServer主机中具备不一样的端点。这是由于每一个外部身份提供者可能使用不一样的协议,而且每一个中间件都在不一样的端点上进行侦听。
全部这些因素的净效果是没有像正常注销工做流那样呈现“注销”页面,这意味着咱们缺乏对IdentityServer客户端的注销通知。咱们必须为每一个联合注销端点添加代码,以呈现必要的通知以实现联合注销。
幸运的是,IdentityServer已经包含此代码。当请求进入IdentityServer和调用外部认证提供商处理,IdentityServer检测,若是这些联合signout要求,若是他们是它会自动呈现相同<iframe>
的这里描述signout。简而言之,自动支持联合注销。
通用架构是所谓的联合网关。在这种方法中,IdentityServer充当一个或多个外部身份提供者的网关。
该架构具备如下优势
您的应用程序只须要了解一个令牌服务(网关),而且不受有关链接到外部提供程序的全部详细信息的影响。这也意味着您能够添加或更改这些外部提供程序,而无需更新您的应用程序。
您控制网关(而不是某些外部服务提供商) - 这意味着您能够对其进行任何更改,并保护您的应用程序免受外部提供程序可能对其本身的服务所作的更改。
大多数外部提供商仅支持一组固定的声明和声明类型 - 在中间具备网关容许对提供商的响应进行后处理以转换/添加/修改特定于域的身份信息。
某些提供商不支持访问令牌(例如社交提供商) - 由于网关知道您的API,它能够根据外部身份发出访问令牌。
某些提供商按您链接的应用程序数收费。网关充当外部提供程序的单个应用程序。在内部,您能够根据须要链接任意数量的应用程序。
一些提供商使用专有协议或对标准协议进行专有修改 - 经过网关,您只须要处理一个地方。
强制每一个身份验证(内部或外部)经过一个地方为身份映射提供极大的灵活性,为您的全部应用程序提供稳定的身份并处理新的需求
换句话说 - 拥有联合网关可让您对身份基础架构进行大量控制。因为您的用户身份是您最重要的资产之一,咱们建议您控制网关。
咱们的快速入门UI使用了如下一些功能。另请参阅外部身份验证快速入门和有关外部提供程序的文档。
您能够经过向IdentityServer应用程序添加身份验证处理程序来添加对外部身份提供程序的支持。
你能够经过调用程序查询这些外部供应商IAuthenticationSchemeProvider
。这容许基于已注册的外部提供程序动态呈现您的登陆页面。
咱们的客户端配置模型容许基于每一个客户端限制可用的提供者(使用该IdentityProviderRestrictions
属性)。
您还可使用EnableLocalLogin
客户端上的属性告诉您的UI是否应该呈现用户名/密码输入。
咱们的快速启动UI漏斗经过一个回调全部外部认证调用(见ExternalLoginCallback
的AccountController
类)。这容许单点进行后处理。
在受权请求期间,若是IdentityServer须要用户赞成,则浏览器将被重定向到赞成页面。
赞成用于容许最终用户授予客户端对资源(身份或API)的访问权限。这一般仅对第三方客户端是必需的,而且能够在客户端设置上按客户端启用/禁用。
为了让用户赞成,托管应用程序必须提供赞成页面。该快速入门UI有一个批准页面的基本实现。
赞成页面一般呈现当前用户的显示名称,请求访问的客户端的显示名称,客户端的徽标,有关客户端的更多信息的连接以及客户端请求访问的资源列表。容许用户代表他们的赞成应该被“记住”也是很常见的,所以未来不会再次提示同一客户。
一旦用户提供了赞成,赞成页面必须通知IdentityServer赞成,而后必须将浏览器重定向回受权端点。
IdentityServer将returnUrl参数(可在用户交互选项上配置)传递到包含受权请求参数的赞成页面。这些参数提供了赞成页面的上下文,能够在交互服务的帮助下阅读。该GetAuthorizationContextAsync
API将返回的实例AuthorizationRequest
。
可使用IClientStore
和IResourceStore
接口获取有关客户端或资源的其余详细信息。
在GrantConsentAsync
对API 的交互服务容许赞成页面知情赞成的结果(这也可能拒绝客户端访问)的IdentityServer。
IdentityServer将暂时保留赞成的结果。这种持久性默认使用cookie,由于它只须要持续足够长的时间来将结果传回给受权端点。这种临时持久性与用于“记住个人赞成”功能的持久性不一样(而且受权端点持续“记住我对用户的赞成”)。若是您但愿在赞成页面和受权重定向之间使用其余一些持久性,那么您能够IMessageStore<ConsentResponse>
在DI中实现并注册实现。
一旦赞成页面通知IdentityServer结果,就能够将用户重定向回returnUrl。您的赞成页面应经过验证returnUrl是否有效来防止打开重定向。这能够经过调用来完成IsValidReturnUrl
的交互服务。此外,若是GetAuthorizationContextAsync
返回非null结果,那么您还能够信任returnUrl有效。
IdentityServer 默认以JWT(JSON Web令牌)格式发出访问令牌。
今天的每一个相关平台都支持验证JWT令牌,这里能够找到一个很好的JWT库列表。热门图书馆例如:
ASP.NET Core的JWT承载认证处理程序
Katana的JWT承载认证中间件
Katana的IdentityServer身份验证中间件
jsonwebtoken为的NodeJS
保护基于ASP.NET核心的API只需在DI中配置JWT承载认证处理程序,并将认证中间件添加到管道:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
// base-address of your identityserver
options.Authority = "https://demo.identityserver.io";
// name of the API resource
options.Audience = "api1";
});
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.UseAuthentication();
app.UseMvc();
}
}
咱们的身份验证处理程序与上面的处理程序具备相同的用途(实际上它在内部使用Microsoft JWT库),但添加了一些其余功能:
支持JWT和参考令牌
用于引用标记的可扩展缓存
统一配置模型
范围验证
对于最简单的状况,咱们的处理程序配置看起来很是相似于上面的代码段:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
// base-address of your identityserver
options.Authority = "https://demo.identityserver.io";
// name of the API resource
options.ApiName = "api1";
});
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.UseAuthentication();
app.UseMvc();
}
}
若是传入令牌不是JWT,咱们的中间件将联系发现文档中的内省端点以验证令牌。因为内省端点须要身份验证,所以您须要提供已配置的API密钥,例如:
.AddIdentityServerAuthentication(options =>
{
// base-address of your identityserver
options.Authority = "https://demo.identityserver.io";
// name of the API resource
options.ApiName = "api1";
options.ApiSecret = "secret";
})
一般,您不但愿为每一个传入请求执行到内省端点的往返。中间件有一个内置缓存,您能够像这样启用:
.AddIdentityServerAuthentication(options =>
{
// base-address of your identityserver
options.Authority = "https://demo.identityserver.io";
// name of the API resource
options.ApiName = "api1";
options.ApiSecret = "secret";
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
})
处理程序将使用在DI容器中注册的任何IDistributedCache实现(例如标准的MemoryDistributedCache)。
所述ApiName属性检查该令牌具备匹配观众(或短aud
),如权利要求。
在IdentityServer中,您还能够将API细分为多个范围。若是须要该粒度,可使用ASP.NET Core受权策略系统来检查范围。
制定全球政策:
services
.AddMvcCore(options =>
{
// require scope1 or scope2
var policy = ScopePolicy.Create("scope1", "scope2");
options.Filters.Add(new AuthorizeFilter(policy));
})
.AddJsonFormatters()
.AddAuthorization();
制定范围政策:
services.AddAuthorization(options =>
{
options.AddPolicy("myPolicy", builder =>
{
// require scope1
builder.RequireScope("scope1");
// and require scope2 or scope3
builder.RequireScope("scope2", "scope3");
});
});
您的身份服务器只是一个标准的ASP.NET核心应用程序,包括IdentityServer中间件。首先阅读有关发布和部署的官方Microsoft 文档。
一般,您将设计IdentityServer部署以实现高可用性:
IdentityServer自己是无状态的,不须要服务器关联 - 可是有些数据须要在实例之间共享。
这一般包括:
资源
客户
启动配置,例如密钥材料,外部提供商设置等......
存储数据的方式取决于您的环境。在配置数据不多更改的状况下,咱们建议使用内存存储和代码或配置文件。
在高度动态的环境(例如Saas)中,咱们建议使用数据库或配置服务动态加载配置。
IdentityServer支持开箱即用的代码配置和配置文件(请参阅此处)。对于数据库,咱们为基于Entity Framework Core的数据库提供支持。
您还能够经过实现IResourceStore
和构建本身的配置存储IClientStore
。
另外一个重要的启动配置是您的主要材料,请参阅此处以获取有关密钥材料和加密的更多详细信息。
对于某些操做,IdentityServer须要持久性存储来保持状态,这包括:
发布受权码
发出引用和刷新令牌
存储赞成
您可使用传统数据库存储操做数据,也可使用具备Redis等持久性功能的缓存。上面提到的EF Core实现也支持运营数据。
您还能够经过实现实现对本身的自定义存储机制的支持IPersistedGrantStore
- 默认状况下IdentityServer会注入内存中的版本。
ASP.NET Core自己须要共享密钥材料来保护cookie,状态字符串等敏感数据。请参阅此处的官方文档。
您能够重复使用上述持久性存储之一,也可使用像共享文件这样的简单文件。
IdentityServer使用ASP.NET Core提供的标准日志记录工具。Microsoft 文档有一个很好的介绍和内置日志记录提供程序的说明。
咱们大体遵循Microsoft使用日志级别的指导原则:
Trace
仅供开发人员解决问题的信息。这些消息可能包含敏感的应用程序数据(如令牌),不该在生产环境中启用。
Debug
遵循内部流程并理解为什么作出某些决定。在开发和调试过程当中具备短时间实用性。
Information
用于跟踪应用程序的通常流程。这些日志一般具备一些长期价值。
Warning
对于应用程序流中的异常或意外事件。这些可能包括错误或其余不会致使应用程序中止但可能须要调查的条件。
Error
对于没法处理的错误和异常。示例:协议请求的验证失败。
Critical
对于须要当即关注的故障。示例:缺乏商店实施,无效的密钥材料......
咱们我的很是喜欢Serilog。试试看。
对于如下配置,您须要Serilog.AspNetCore
和Serilog.Sinks.Console
包:
public class Program
{
public static void Main(string[] args)
{
Console.Title = "IdentityServer4";
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();
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog()
.Build();
}
}
对于如下配置,您须要Serilog.Extensions.Logging
和Serilog.Sinks.Console
包:
public class Program
{
public static void Main(string[] args)
{
Console.Title = "IdentityServer4";
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();
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureLogging(builder =>
{
builder.ClearProviders();
builder.AddSerilog();
})
.Build();
}
}
日志记录是更低级别的“printf”样式 - 事件表明有关IdentityServer中某些操做的更高级别信息。事件是结构化数据,包括事件ID,成功/失败信息,类别和详细信息。这使得查询和分析它们变得容易,并提取可用于进一步处理的有用信息。
默认状况下不会启用事件 - 但能够在ConfigureServices
方法中进行全局配置,例如:
services.AddIdentityServer(options =>
{
options.Events.RaiseSuccessEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseErrorEvents = true;
});
要发出事件,请使用IEventService
DI容器并调用RaiseAsync
方法,例如:
public async Task<IActionResult> Login(LoginInputModel model)
{
if (_users.ValidateCredentials(model.Username, model.Password))
{
// issue authentication cookie with subject ID and username
var user = _users.FindByUsername(model.Username);
await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username));
}
else
{
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"));
}
}
咱们的默认事件接收器只是将事件类序列化为JSON并将其转发到ASP.NET Core日志系统。若是要链接到自定义事件存储,请实现该IEventSink
接口并将其注册到DI。
如下示例使用Seq发出事件:
public class SeqEventSink : IEventSink
{
private readonly Logger _log;
public SeqEventSink()
{
_log = new LoggerConfiguration()
.WriteTo.Seq("http://localhost:5341")
.CreateLogger();
}
public Task PersistAsync(Event evt)
{
if (evt.EventType == EventTypes.Success ||
evt.EventType == EventTypes.Information)
{
_log.Information("{Name} ({Id}), Details: {@details}",
evt.Name,
evt.Id,
evt);
}
else
{
_log.Error("{Name} ({Id}), Details: {@details}",
evt.Name,
evt.Id,
evt);
}
return Task.CompletedTask;
}
}
将Serilog.Sinks.Seq
包添加到主机以使上述代码有效。
IdentityServer中定义了如下事件:
ApiAuthenticationFailureEvent
& ApiAuthenticationSuccessEvent
获取内省端点上成功/失败的API身份验证。
ClientAuthenticationSuccessEvent
& ClientAuthenticationFailureEvent
获取在令牌端点处成功/失败的客户端身份验证。
TokenIssuedSuccessEvent
& TokenIssuedFailureEvent
获取成功/失败尝试请求身份令牌,访问令牌,刷新令牌和受权码。
TokenIntrospectionSuccessEvent
& TokenIntrospectionFailureEvent
获取成功的令牌内省请求。
TokenRevokedSuccessEvent
获取成功的令牌吊销请求。
UserLoginSuccessEvent
& UserLoginFailureEvent
成功/失败用户登陆的快速入门UI引起。
UserLogoutSuccessEvent
获取成功的注销请求。
ConsentGrantedEvent
& ConsentDeniedEvent
在赞成UI中引起。
UnhandledExceptionEvent
获取未处理的异常。
您能够建立本身的事件并经过咱们的基础架构发出它们。
您须要从咱们的基Event
类派生,该基类注入活动ID,时间戳等上下文信息。您的派生类能够添加特定于事件上下文的任意数据字段:
public class UserLoginFailureEvent : Event
{
public UserLoginFailureEvent(string username, string error)
: base(EventCategories.Authentication,
"User Login Failure",
EventTypes.Failure,
EventIds.UserLoginFailure,
error)
{
Username = username;
}
public string Username { get; set; }
}
IdentityServer依赖于几个加密机制来完成其工做。
IdentityServer须要非对称密钥对来签署和验证JWT。此密钥对能够是证书/私钥组合或原始RSA密钥。不管如何,它必须支持带有SHA256的RSA。
加载签名密钥和相应的验证部分是经过ISigningCredentialStore
和的实现来完成的IValidationKeysStore
。若是要自定义加载密钥,能够实现这些接口并将其注册到DI。
DI构建器扩展有几种方便的方法来设置签名和验证密钥 - 请参阅此处。
虽然一次只能使用一个签名密钥,但您能够将多个验证密钥发布到发现文档。这对于密钥翻转颇有用。
翻转一般以下所示:
您请求/建立新的密钥材料
除了当前的验证密钥以外,还要发布新的验证密钥。您可使用AddValidationKeys
构建器扩展方法。
全部客户端和API如今都有机会在下次更新发现文档的本地副本时了解新密钥
在必定时间(例如24小时)以后,全部客户端和API如今应该接受旧密钥材料和新密钥材料
只要你愿意,就能够保留旧的密钥材料,也许你有须要验证的长寿命令牌
当旧密钥材料再也不使用时,将其退出
全部客户端和API将在下次更新发现文档的本地副本时“忘记”旧密钥
这要求客户端和API使用发现文档,而且还具备按期刷新其配置的功能。
ASP.NET Core中的Cookie身份验证(或MVC中的防伪)使用ASP.NET Core数据保护功能。根据您的部署方案,这可能须要其余配置。有关更多信息,请参阅Microsoft 文档。
咱们不强制使用HTTPS,但对于生产,它必须与IdentityServer进行每次交互。
受权类型是指定客户端如何与IdentityServer交互的方式。OpenID Connect和OAuth 2规范定义了如下受权类型:
含蓄
受权码
混合动力
客户凭证
资源全部者密码
刷新令牌
延期拨款
您能够经过配置上的AllowedGrantTypes
属性指定客户端可使用的受权类型Client
。
能够将客户端配置为使用多个受权类型(例如,用于以用户为中心的操做的混合和用于服务器到服务器通讯的客户端凭证)。的GrantTypes
类能够用来从典型交付式的组合,以挑选:
Client.AllowedGrantTypes = GrantTypes.HybridAndClientCredentials;
您也能够手动指定受权类型列表:
Client.AllowedGrantTypes =
{
GrantType.Hybrid,
GrantType.ClientCredentials,
"my_custom_grant_type"
};
若是要经过浏览器通道传输访问令牌,还须要在客户端配置上明确容许:
Client.AllowAccessTokensViaBrowser = true;
注意
出于安全缘由,并不是全部受权类型组合都是容许的。请参阅下面的更多细节。
对于其他部分,简要描述了受权类型,以及什么时候使用它们。还建议您另外阅读相应的规格以更好地理解差别。
这是最简单的受权类型,用于服务器到服务器通讯 - 始终表明客户端而不是用户请求令牌。
使用此受权类型,您能够向令牌端点发送令牌请求,并获取表明客户端的访问令牌。客户端一般必须使用其客户端ID和密钥对令牌端点进行身份验证。
有关如何使用它的示例,请参阅“ 客户端凭据快速入门 ”。
资源全部者密码授予类型容许经过将用户的名称和密码发送到令牌端点来表明用户请求令牌。这就是所谓的“非交互式”身份验证,一般不推荐使用。
某些遗留或第一方集成方案可能有缘由,其中此受权类型颇有用,但通常建议使用隐式或混合的交互式流来代替用户身份验证。
有关如何使用它的示例,请参阅资源全部者密码快速入门。您还须要提供用户名/密码验证的代码,能够经过实现IResourceOwnerPasswordValidator
接口来提供。您能够在此处找到有关此界面的更多信息。
隐式受权类型针对基于浏览器的应用程序进行了优化。仅用于用户身份验证(服务器端和JavaScript应用程序),或身份验证和访问令牌请求(JavaScript应用程序)。
在隐式流程中,全部令牌都经过浏览器传输,所以不容许使用刷新令牌等高级功能。
该快速入门显示了服务端的web应用程序的认证,而 这个显示的JavaScript。
受权代码流最初由OAuth 2指定,并提供了一种在反向通道上检索令牌而不是浏览器前端通道的方法。它还支持客户端身份验证。
虽然这种受权类型自己是受支持的,但一般建议您将其与身份令牌结合使用,将其转换为所谓的混合流。混合流程为您提供重要的额外功能,如签名协议响应。
混合流是隐式和受权代码流的组合 - 它使用多种受权类型的组合,最典型的是。code id_token
在混合流中,身份令牌经过浏览器通道传输,并包含签名协议响应以及其余工件(如受权代码)的签名。这减轻了许多适用于浏览器通道的攻击。成功验证响应后,反向通道用于检索访问和刷新令牌。
这是但愿检索访问令牌(也多是刷新令牌)的本机应用程序的推荐流程,用于服务器端Web应用程序和本机桌面/移动应用程序。
有关将混合流与MVC一块儿使用的更多信息,请参阅此快速入门。
刷新令牌容许得到对API的长期访问。
您一般但愿尽量缩短访问令牌的生命周期,但同时不但愿经过对IdentityServer进行前端通道往返来请求新的令牌一次又一次地打扰用户。
刷新令牌容许在没有用户交互的状况下请求新的访问令牌。每次客户端刷新令牌时,都须要对IdentityServer进行(通过身份验证的)反向通道调用。这容许检查刷新令牌是否仍然有效,或者在此期间是否已被撤销。
混合,受权代码和资源全部者密码流支持刷新令牌。要请求刷新令牌,客户端须要offline_access
在令牌请求中包含范围(而且必须被受权请求该范围)。
扩展受权容许使用新的受权类型扩展令牌端点。有关详细信息,请参阅此
禁止使用某些受权类型组合:
混合隐式和受权代码或混合将容许从更安全的基于代码的流的降级攻击到隐式。
一样存在容许受权代码和混合代码的问题
在某些状况下,客户端须要使用身份服务器进行身份验证,例如
在令牌端点请求令牌的机密应用程序(也称为客户端)
API在内省端点验证引用令牌
为此,您能够将秘密列表分配给客户端或API资源。
秘密解析和验证是身份服务器中的可扩展点,开箱即用它支持共享机密以及经过基自己份验证头或POST主体传输共享机密。
如下代码设置散列共享密钥:
var secret = new Secret("secret".Sha256());
如今能够将此秘密分配给a Client
或an ApiResource
。请注意,它们不只支持单个秘密,还支持多个秘密。这对于秘密翻转和轮换很是有用:
var client = new Client
{
ClientId = "client",
ClientSecrets = new List<Secret> { secret },
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes = new List<string>
{
"api1", "api2"
}
};
实际上,您还能够为秘密分配说明和到期日期。该描述将用于记录,以及强制执行秘密生存期的到期日期:
var secret = new Secret(
"secret".Sha256(),
"2016 secret",
new DateTime(2016, 12, 31));
您能够将客户端ID /机密组合做为POST正文的一部分发送:
POST /connect/token
client_id=client1&
client_secret=secret&
...
..或做为基自己份验证标头:
POST /connect/token
Authorization: Basic xxxxx
...
您可使用如下C#代码手动建立基自己份验证标头:
var credentials = string.Format("{0}:{1}", clientId, clientSecret);
var headerValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials));
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", headerValue);
该IdentityModel库有一个叫作辅助类TokenClient
和IntrospectionClient
封装认证和协议消息。
还有其余技术来验证客户端,例如基于公钥/私钥加密。IdentityServer包括对私钥JWT客户机密钥的支持(请参阅RFC 7523)。
秘密可扩展性一般包含三件事:
一个秘密的定义
一个知道如何从传入请求中提取秘密的秘密解析器
一个秘密验证器,知道如何根据定义验证解析的秘密
秘密解析器和验证器是ISecretParser
和ISecretValidator
接口的实现。要使它们可用于IdentityServer,您须要将它们注册到DI容器,例如:
builder.AddSecretParser<ClientAssertionSecretParser>()
builder.AddSecretValidator<PrivateKeyJwtSecretValidator>()
咱们的默认私钥JWT秘密验证器指望完整(叶)证书做为秘密定义的base64。而后,此证书将用于验证自签名JWT上的签名,例如:
var client = new Client
{
ClientId = "client.jwt",
ClientSecrets =
{
new Secret
{
Type = IdentityServerConstants.SecretTypes.X509CertificateBase64,
Value = "MIIDATCCAe2gAwIBAgIQoHUYAquk9rBJcq8W+F0FAzAJBgUrDgMCHQUAMBIxEDAOBgNVBAMTB0RldlJvb3QwHhcNMTAwMTIwMjMwMDAwWhcNMjAwMTIwMjMwMDAwWjARMQ8wDQYDVQQDEwZDbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSaY4x1eXqjHF1iXQcF3pbFrIbmNw19w/IdOQxbavmuPbhY7jX0IORu/GQiHjmhqWt8F4G7KGLhXLC1j7rXdDmxXRyVJBZBTEaSYukuX7zGeUXscdpgODLQVay/0hUGz54aDZPAhtBHaYbog+yH10sCXgV1Mxtzx3dGelA6pPwiAmXwFxjJ1HGsS/hdbt+vgXhdlzud3ZSfyI/TJAnFeKxsmbJUyqMfoBl1zFKG4MOvgHhBjekp+r8gYNGknMYu9JDFr1ue0wylaw9UwG8ZXAkYmYbn2wN/CpJl3gJgX42/9g87uLvtVAmz5L+rZQTlS1ibv54ScR2lcRpGQiQav/LAgMBAAGjXDBaMBMGA1UdJQQMMAoGCCsGAQUFBwMCMEMGA1UdAQQ8MDqAENIWANpX5DZ3bX3WvoDfy0GhFDASMRAwDgYDVQQDEwdEZXZSb290ghAsWTt7E82DjU1E1p427Qj2MAkGBSsOAwIdBQADggEBADLje0qbqGVPaZHINLn+WSM2czZk0b5NG80btp7arjgDYoWBIe2TSOkkApTRhLPfmZTsaiI3Ro/64q+Dk3z3Kt7w+grHqu5nYhsn7xQFAQUf3y2KcJnRdIEk0jrLM4vgIzYdXsoC6YO+9QnlkNqcN36Y8IpSVSTda6gRKvGXiAhu42e2Qey/WNMFOL+YzMXGt/nDHL/qRKsuXBOarIb++43DV3YnxGTx22llhOnPpuZ9/gnNY7KLjODaiEciKhaKqt/b57mTEz4jTF4kIg6BP03MUfDXeVlM1Qf1jB43G2QQ19n5lUiqTpmQkcfLfyci2uBZ8BkOhXr3Vk9HIk/xBXQ="
}
},
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes = { "api1", "api2" }
};
您能够实现本身的秘密验证器(或扩展咱们的秘密验证器)来实现例如链信任验证。
OAuth 2.0定义了令牌端点的标准受权类型,例如password
,authorization_code
和refresh_token
。扩展受权是一种添加对非标准令牌颁发方案(如令牌转换,委派或自定义凭据)的支持的方法。
您能够经过实现IExtensionGrantValidator
接口添加对其余受权类型的支持:
public interface IExtensionGrantValidator
{
/// <summary>
/// Handles the custom grant request.
/// </summary>
/// <param name="request">The validation context.</param>
Task ValidateAsync(ExtensionGrantValidationContext context);
/// <summary>
/// Returns the grant type this validator can deal with
/// </summary>
/// <value>
/// The type of the grant.
/// </value>
string GrantType { get; }
}
该ExtensionGrantValidationContext
对象使您能够访问:
传入令牌请求 - 众所周知的验证值,以及任何自定义值(经过Raw
集合)
结果 - 错误或成功
自定义响应参数
要注册扩展受权,请将其添加到DI:
builder.AddExtensionGrantValidator<MyExtensionsGrantValidator>();
想象一下如下场景 - 前端客户端使用经过交互流(例如混合流)获取的令牌调用中间层API。此中间层API(API 1)如今但愿表明交互式用户调用后端API(API 2):
换句话说,中间层API(API 1)须要包含用户身份的访问令牌,但须要具备后端API(API 2)的范围。
注意
您可能据说过穷人表明团这一术语,前端的访问令牌只是转发到后端。这有一些缺点,例如API 2如今必须接受API 1范围,这将容许用户直接调用API 2。此外 - 您可能但愿在令牌中添加一些特定于委托的声明,例如,呼叫路径是经过API 1的事实。
实施扩展受权
前端会将令牌发送到API 1,如今须要在IdentityServer上使用API 2的新令牌交换此令牌。
在线上,对交换的令牌服务的调用可能以下所示:
POST /connect/token
grant_type=delegation&
scope=api2&
token=...&
client_id=api1.client
client_secret=secret
扩展受权验证程序的工做是经过验证传入令牌并返回表示新令牌的结果来处理该请求:
public class DelegationGrantValidator : IExtensionGrantValidator
{
private readonly ITokenValidator _validator;
public DelegationGrantValidator(ITokenValidator validator)
{
_validator = validator;
}
public string GrantType => "delegation";
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
var userToken = context.Request.Raw.Get("token");
if (string.IsNullOrEmpty(userToken))
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
var result = await _validator.ValidateAccessTokenAsync(userToken);
if (result.IsError)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
// get user's identity
var sub = result.Claims.FirstOrDefault(c => c.Type == "sub").Value;
context.Result = new GrantValidationResult(sub, GrantType);
return;
}
}
不要忘记在DI上注册验证器。
注册委托客户端
您须要在IdentityServer中进行客户端注册,以容许客户端使用此新的扩展受权,例如:
var client = new client
{
ClientId = "api1.client",
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
},
AllowedGrantTypes = { "delegation" },
AllowedScopes = new List<string>
{
"api2"
}
}
调用令牌端点
在API 1中,您如今能够本身构建HTTP有效内容,或使用IdentityModel帮助程序库:
public async Task<TokenResponse> DelegateAsync(string userToken)
{
var payload = new
{
token = userToken
};
// create token client
var client = new TokenClient(disco.TokenEndpoint, "api1.client", "secret");
// send custom grant to token endpoint, return response
return await client.RequestCustomGrantAsync("delegation", "api2", payload);
}
如今TokenResponse.AccessToken
将包含委托访问令牌。
若是要使用OAuth 2.0资源全部者密码凭据受权(aka password
),则须要实现并注册IResourceOwnerPasswordValidator
接口:
public interface IResourceOwnerPasswordValidator
{
/// <summary>
/// Validates the resource owner password credential
/// </summary>
/// <param name="context">The context.</param>
Task ValidateAsync(ResourceOwnerPasswordValidationContext context);
}
在上下文中,您将找到已解析的协议参数,如UserName
和Password
,以及原始请求,若是您想查看其余输入数据。
而后,您的工做是实施密码验证并相应地设置Result
上下文。请参阅GrantValidationResult文档
因为访问令牌的生命周期有限,所以刷新令牌容许在没有用户交互的状况下请求新的访问令牌。
如下流程支持刷新令牌:受权代码,混合和资源全部者密码凭据流。须要明确受权客户端经过设置AllowOfflineAccess
来请求刷新令牌true
。
AbsoluteRefreshTokenLifetime
刷新令牌的最长生命周期,以秒为单位。默认为2592000秒/ 30天。零容许刷新令牌,当仅在SlidingRefreshTokenLifetime传递后使用时过时。RefreshTokenExpiration = Sliding
SlidingRefreshTokenLifetime
刷新令牌的生命周期以秒为单位。默认为1296000秒/ 15天
RefreshTokenUsage
ReUse
刷新令牌时刷新令牌句柄将保持不变OneTime
刷新令牌时将更新刷新令牌句柄
RefreshTokenExpiration
Absolute
刷新令牌将在固定时间点到期(由AbsoluteRefreshTokenLifetime指定)Sliding
刷新令牌时,将刷新刷新令牌的生命周期(按SlidingRefreshTokenLifetime中指定的数量)。生命周期不会超过AbsoluteRefreshTokenLifetime。
UpdateAccessTokenClaimsOnRefresh
获取或设置一个值,该值指示是否应在刷新令牌请求上更新访问令牌(及其声明)。
访问令牌能够有两种形式 - 自包含或参考。
JWT令牌将是一个自包含的访问令牌 - 它是一个带有声明和过时的受保护数据结构。一旦API了解了密钥材料,它就能够验证自包含的令牌,而无需与发行者进行通讯。这使得JWT难以撤销。它们将一直有效,直到它们过时。
使用引用令牌时 - IdentityServer会将令牌的内容存储在数据存储中,而且只会将此令牌的惟一标识符发回给客户端。接收此引用的API必须打开与IdentityServer的反向通道通讯以验证令牌。
您可使用如下设置切换客户端的令牌类型:
client.AccessTokenType = AccessTokenType.Reference;
IdentityServer提供了OAuth 2.0内省规范的实现,该规范容许API取消引用令牌。您可使用咱们的专用内省中间件 或使用身份服务器身份验证中间件,它能够验证JWT和引用令牌。
内省端点须要身份验证 - 由于内省端点的客户端是API,您能够在如下位置配置秘密ApiResource
:
var api = new ApiResource("api1")
{
ApiSecrets = { new Secret("secret".Sha256()) }
}
有关如何为API配置IdentityServer身份验证中间件的详细信息,请参阅此处。
IdentityServer中的许多端点将经过基于JavaScript的客户端的Ajax调用进行访问。鉴于IdentityServer最有可能托管在与这些客户端不一样的源上,这意味着须要配置跨源资源共享(CORS)。
配置CORS的一种方法是AllowedCorsOrigins
在客户端配置上使用该集合。只需将客户端的原点添加到集合中,IdentityServer中的默认配置将查询这些值以容许来自源的跨源调用。
注意
配置CORS时,请务必使用原点(不是URL)。例如:https://foo:123/
是一个URL,而是https://foo:123
一个原点。
若是您使用咱们提供的“内存中”或基于EF的客户端配置,则将使用此默认CORS实现。若是您定义本身的IClientStore
,那么您将须要实现本身的自定义CORS策略服务(见下文)。
IdentityServer容许托管应用程序实现ICorsPolicyService
彻底控制CORS策略。
要实现的单一方法是:。若是容许原点则返回,不然返回。Task<bool> IsOriginAllowedAsync(string origin)``true``false
实现后,只需在DI中注册实现,而后IdentityServer将使用您的自定义实现。
DefaultCorsPolicyService
若是您只是但愿硬编码一组容许的原点,那么ICorsPolicyService
可使用一个预先构建的实现调用DefaultCorsPolicyService
。这将被配置为DI单身,并以其硬编码的AllowedOrigins
收集,或设置标志AllowAll
,以true
容许全部的起源。例如,在ConfigureServices
:
var cors = new DefaultCorsPolicyService(_loggerFactory.CreateLogger<DefaultCorsPolicyService>())
{
AllowedOrigins = { "https://foo", "https://bar" }
};
services.AddSingleton<ICorsPolicyService>(cors);
注意
AllowAll
谨慎使用。
IdentityServer使用ASP.NET Core的CORS中间件来提供其CORS实现。托管IdentityServer的应用程序可能还须要CORS用于本身的自定义端点。一般,二者应该在同一个应用程序中一块儿工做。
您的代码应使用ASP.NET Core中记录的CORS功能,而不考虑IdentityServer。这意味着您应该定义策略并正常注册中间件。若是您的应用程序定义了策略ConfigureServices
,那么这些策略应该继续在您使用它们的相同位置(在您配置CORS中间件的位置或在EnableCors
控制器代码中使用MVC 属性的位置)。相反,若是您使用CORS中间件(经过策略构建器回调)定义内联策略,那么它也应该继续正常工做。
在您使用ASP.NET Core CORS服务和IdentityServer之间可能存在冲突的一种状况是,若是您决定建立自定义ICorsPolicyProvider
。鉴于ASP.NET Core的CORS服务和中间件的设计,IdentityServer实现了本身的自定义ICorsPolicyProvider
并将其注册到DI系统中。幸运的是,IdentityServer实现旨在使用装饰器模式来包装ICorsPolicyProvider
已在DI中注册的任何现有模式 。这意味着你也能够实现ICorsPolicyProvider
,但它只须要在DI中的IdentityServer以前注册(例如,在ConfigureServices
)。
能够在https://baseaddress/.well-known/openid-configuration找到发现文档。它包含有关IdentityServer的端点,密钥材料和功能的信息。
默认状况下,全部信息都包含在发现文档中,但经过使用配置选项,您能够隐藏各个部分,例如:
services.AddIdentityServer(options =>
{
options.Discovery.ShowIdentityScopes = false;
options.Discovery.ShowApiScopes = false;
options.Discovery.ShowClaims = false;
options.Discovery.ShowExtensionGrantTypes = false;
});
您能够向发现文档添加自定义条目,例如:
services.AddIdentityServer(options =>
{
options.Discovery.CustomEntries.Add("my_setting", "foo");
options.Discovery.CustomEntries.Add("my_complex_setting",
new
{
foo = "foo",
bar = "bar"
});
});
当您添加以〜开头的自定义值时,它将扩展到IdentityServer基址如下的绝对路径,例如:
options.Discovery.CustomEntries.Add("my_custom_endpoint", "~/custom");
若是要彻底控制发现(和jwks)文档的呈现,能够实现IDiscoveryResponseGenerator
接口(或从咱们的默认实现派生)。
您能够向托管IdentityServer4的应用程序添加更多API端点。
您一般但愿经过它们所托管的IdentityServer实例来保护这些API。这不是问题。只需将令牌验证处理程序添加到主机(请参阅此处):
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// details omitted
services.AddIdentityServer();
services.AddAuthentication()
.AddIdentityServerAuthentication("token", isAuth =>
{
isAuth.Authority = "base_address_of_identityserver";
isAuth.ApiName = "name_of_api";
});
}
在您的API上,您须要添加[Authorize]
属性并显式引用您要使用的身份验证方案(token
在此示例中,您能够选择您喜欢的任何名称):
public class TestController : ControllerBase
{
[Route("test")]
[Authorize(AuthenticationSchemes = "token")]
public IActionResult Get()
{
var claims = User.Claims.Select(c => new { c.Type, c.Value }).ToArray();
return Ok(new { message = "Hello API", claims });
}
}
若是要从浏览器调用该API,则还须要配置CORS(请参阅此处)。
若是须要,您还能够将端点添加到发现文档中,例如:
services.AddIdentityServer(options =>
{
options.Discovery.CustomEntries.Add("custom_endpoint", "~/api/custom");
})
除了对OpenID Connect和OAuth 2.0的内置支持以外,IdentityServer4还容许添加对其余协议的支持。
您能够将这些附加协议端点添加为中间件或使用例如MVC控制器。在这两种状况下,您均可以访问ASP.NET Core DI系统,该系统容许重用咱们的内部服务,例如访问客户端定义或密钥材料。
能够在此处找到添加WS-Federation支持的示例。
身份验证请求一般以下所示:
身份验证请求到达协议端点
协议端点执行输入验证
重定向到登陆页面,返回URL设置回协议端点(若是用户是匿名的)
经过访问当前请求详细信息 IIdentityServerInteractionService
用户身份验证(本地或经过外部身份验证中间件)登陆用户重定向回协议端点
建立协议响应(令牌建立和重定向回客户端)
要实现上述工做流程,须要与IdentityServer创建一些交互点。
访问配置并重定向到登陆页面
您能够经过将IdentityServerOptions
类注入代码来访问IdentityServer配置。这个,例如具备登陆页面的配置路径:
var returnUrl = Url.Action("Index");
returnUrl = returnUrl.AddQueryString(Request.QueryString.Value);
var loginUrl = _options.UserInteraction.LoginUrl;
var url = loginUrl.AddQueryString(_options.UserInteraction.LoginReturnUrlParameter, returnUrl);
return Redirect(url);
登陆页面与当前协议请求之间的交互
所述IIdentityServerInteractionService
支撑件转动一个协议返回URL成解析和验证上下文对象:
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
默认状况下,交互服务仅了解OpenID Connect协议消息。要扩展支持,您能够本身编写IReturnUrlParser
:
public interface IReturnUrlParser
{
bool IsValidReturnUrl(string returnUrl);
Task<AuthorizationRequest> ParseAsync(string returnUrl);
}
..而后在DI中注册解析器:
builder.Services.AddTransient<IReturnUrlParser, WsFederationReturnUrlParser>();
这容许登陆页面获取客户端配置和其余协议参数等信息。
访问用于建立协议响应的配置和密钥材料
经过将IKeyMaterialService
代码注入到代码中,您能够访问配置的签名凭据和验证密钥:
工具
该IdentityServerTools班是为IdentityServer编写扩展代码时,你可能须要有效的内部工具的集合。要使用它,请将其注入代码,例如控制器:
public MyController(IdentityServerTools tools)
{
_tools = tools;
}
该IssueJwtAsync方法容许使用IdentityServer令牌建立引擎建立JWT令牌。这IssueClientJwtAsync是用于为服务器到服务器通讯建立令牌的简单版本(例如,当您必须从代码中调用受IdentityServer保护的API时):
public async Task<IActionResult> MyAction()
{
var token = await _tools.IssueClientJwtAsync(
clientId: "client_id",
lifetime: 3600,
audiences: new[] { "backend.api" });
// more code
}var credential = await _keys.GetSigningCredentialsAsync();
var key = credential.Key as Microsoft.IdentityModel.Tokens.X509SecurityKey;
var descriptor = new SecurityTokenDescriptor
{
AppliesToAddress = result.Client.ClientId,
Lifetime = new Lifetime(DateTime.UtcNow, DateTime.UtcNow.AddSeconds(result.Client.IdentityTokenLifetime)),
ReplyToAddress = result.Client.RedirectUris.First(),
SigningCredentials = new X509SigningCredentials(key.Certificate, result.RelyingParty.SignatureAlgorithm, result.RelyingParty.DigestAlgorithm),
Subject = outgoingSubject,
TokenIssuerName = _contextAccessor.HttpContext.GetIdentityServerIssuerUri(),
TokenType = result.RelyingParty.TokenType
};
发现端点可用于检索有关IdentityServer的元数据 - 它返回诸如颁发者名称,密钥材料,支持的范围等信息。有关详细信息,请参阅规范。
发现端点可经过/.well-known/openid-configuration相对于基地址得到,例如:
https://demo.identityserver.io/.well-known/openid-configuration
注意
您可使用IdentityModel客户端库以编程方式从.NET代码访问发现端点。有关更多信息,请查看IdentityModel 文档。
受权端点可用于经过浏览器请求令牌或受权码。此过程一般涉及最终用户的身份验证和可选的赞成。
注意
IdentityServer支持OpenID Connect和OAuth 2.0受权请求参数的子集。有关完整列表,请参见此处。
client_id
客户的标识符(必填)。
scope
一个或多个注册范围(必填)
redirect_uri
必须与该客户端容许的重定向URI之一彻底匹配(必需)
response_type
id_token
请求身份令牌(仅容许身份范围)token
请求访问令牌(仅容许资源范围)id_token token
请求身份令牌和访问令牌code
请求受权码code id_token
请求受权代码和身份令牌code id_token token
请求受权代码,身份令牌和访问令牌
response_mode
form_post
将令牌响应做为表单发送而不是片断编码重定向(可选)
state
identityserver将回显令牌响应的状态值,这是客户端和提供者之间的往返状态,关联请求和响应以及CSRF /重放保护。(推荐的)
nonce
identityserver将回显身份令牌中的nonce值,这是为了重放保护)经过隐式受权对身份令牌是必需的。
prompt
none
请求期间不会显示任何UI。若是这是不可能的(例如,由于用户必须登陆或赞成),则返回错误login
即便用户已登陆并具备有效会话,也会显示登陆UI
code_challenge
发送PKCE的代码质询
code_challenge_method
plain
表示挑战是使用纯文本(不推荐) S256
表示使用SHA256对挑战进行哈希处理
login_hint
可用于预先填写登陆页面上的用户名字段
ui_locales
提供有关登陆UI所需显示语言的提示
max_age
若是用户的登陆会话超过最大年龄(以秒为单位),将显示登陆UI
acr_values
容许传递额外的身份验证相关信息 - 身份服务器特殊状况下面的私有acr_values:idp:name_of_idp
绕过login / home领域屏幕并将用户直接转发到选定的身份提供者(若是容许每一个客户端配置)tenant:name_of_tenant
可用于将租户名称传递给登陆UI
例
GET /connect/authorize?
client_id=client1&
scope=openid email api1&
response_type=id_token token&
redirect_uri=https://myapp/callback&
state=abc&
nonce=xyz
(删除了URL编码,并添加了换行符以提升可读性)
注意
您可使用IdentityModel客户端库以编程方式建立受权请求.NET代码。有关更多信息,请查看IdentityModel 文档。
令牌端点可用于以编程方式请求令牌。它支持password
,authorization_code
,client_credentials
和refresh_token
补助的类型)。此外,能够扩展令牌端点以支持扩展受权类型。
注意
IdentityServer支持OpenID Connect和OAuth 2.0令牌请求参数的子集。有关完整列表,请参见此处。
client_id
客户标识符(必填)
client_secret
客户端密钥能够在帖子正文中,也能够做为基自己份验证标头。可选的。
grant_type
authorization_code
,client_credentials
,password
,refresh_token
或自定义
scope
一个或多个注册范围。若是未指定,将发出全部明确容许范围的标记。
redirect_uri
authorization_code
受权类型所需
code
受权码(authorization_code
受权类型所需)
code_verifier
PKCE证实密钥
username
资源全部者用户名(password
授予类型所需)
password
资源全部者密码(password
授予类型所需)
acr_values
容许为password
受权类型传递额外的身份验证相关信息- identityserver特殊状况下面的专有acr_values:idp:name_of_idp
绕过login / home领域屏幕并将用户直接转发到选定的身份提供者(若是容许每一个客户端配置)tenant:name_of_tenant
可用于将租户名称传递给令牌端点
refresh_token
刷新令牌(refresh_token
授予类型所需)
POST /connect/token
client_id=client1&
client_secret=secret&
grant_type=authorization_code&
code=hdh922&
redirect_uri=https://myapp.com/callback
(为了便于阅读,删除了表格编码并添加了换行符)
注意
您可使用IdentityModel客户端库以编程方式从.NET代码访问令牌端点。有关更多信息,请查看IdentityModel 文档。
UserInfo端点可用于检索有关用户的身份信息(请参阅规范)。
调用者须要发送表明用户的有效访问令牌。根据授予的范围,UserInfo端点将返回映射的声明(至少须要openid做用域)。
GET /connect/userinfo
Authorization: Bearer <access_token>
HTTP/1.1 200 OK
Content-Type: application/json
{
"sub": "248289761001",
"name": "Bob Smith",
"given_name": "Bob",
"family_name": "Smith",
"role": [
"user",
"admin"
]
}
注意
您可使用IdentityModel客户端库以编程方式从.NET代码访问userinfo端点。有关更多信息,请查看IdentityModel 文档。
内省端点是RFC 7662的实现。
它可用于验证引用令牌(若是消费者不支持适当的JWT或加密库,则可使用JWT)。内省端点须要身份验证 - 由于内省端点的客户端是API,您能够在上配置密码ApiResource
。
POST /connect/introspect
Authorization: Basic xxxyyy
token=<token>
成功的响应将返回状态代码200以及活动或非活动令牌:
{
"active": true,
"sub": "123"
}
未知或过时的令牌将被标记为无效:
{
"active": false,
}
无效请求将返回400,未受权请求401。
注意
您可使用IdentityModel客户端库以编程方式从.NET代码访问内省端点。有关更多信息,请查看IdentityModel 文档。
此端点容许撤消访问令牌(仅限引用令牌)和刷新令牌。它实现了令牌撤销规范(RFC 7009)。
token
要撤销的令牌(必填)
token_type_hint
或者access_token
或refresh_token
(可选)
POST /connect/revocation HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token
注意
您可使用IdentityModel客户端库以编程方式从.NET代码访问吊销端点。有关更多信息,请查看IdentityModel 文档。
结束会话端点可用于触发单点注销(请参阅规范)。
要使用结束会话端点,客户端应用程序会将用户的浏览器重定向到结束会话URL。用户在会话期间经过浏览器登陆的全部应用程序均可以参与注销。
注意
终端会话端点的URL可经过发现端点得到。
id_token_hint
当用户被重定向到端点时,系统会提示他们是否真的想要注销。发送从身份验证收到的原始id_token的客户端能够绕过此提示。这是做为查询的字符串参数传递的id_token_hint
。
post_logout_redirect_uri
若是id_token_hint
传递了有效,则客户端也能够发送post_logout_redirect_uri
参数。这可用于容许用户在注销后重定向回客户端。该值必须与客户端预先配置的PostLogoutRedirectUris(客户端文档)之一匹配。
州
若是post_logout_redirect_uri
传递了有效,则客户端也能够发送state
参数。在用户重定向回客户端后,这将做为查询字符串参数返回给客户端。这一般由客户端用于跨重定向的往返状态。
GET /connect/endsession?id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6IjdlOGFkZmMzMjU1OTEyNzI0ZDY4NWZmYmIwOThjNDEyIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0OTE3NjUzMjEsImV4cCI6MTQ5MTc2NTYyMSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoianNfb2lkYyIsIm5vbmNlIjoiYTQwNGFjN2NjYWEwNGFmNzkzNmJjYTkyNTJkYTRhODUiLCJpYXQiOjE0OTE3NjUzMjEsInNpZCI6IjI2YTYzNWVmOTQ2ZjRiZGU3ZWUzMzQ2ZjFmMWY1NTZjIiwic3ViIjoiODg0MjExMTMiLCJhdXRoX3RpbWUiOjE0OTE3NjUzMTksImlkcCI6ImxvY2FsIiwiYW1yIjpbInB3ZCJdfQ.STzOWoeVYMtZdRAeRT95cMYEmClixWkmGwVH2Yyiks9BETotbSZiSfgE5kRh72kghN78N3-RgCTUmM2edB3bZx4H5ut3wWsBnZtQ2JLfhTwJAjaLE9Ykt68ovNJySbm8hjZhHzPWKh55jzshivQvTX0GdtlbcDoEA1oNONxHkpDIcr3pRoGi6YveEAFsGOeSQwzT76aId-rAALhFPkyKnVc-uB8IHtGNSyRWLFhwVqAdS3fRNO7iIs5hYRxeFSU7a5ZuUqZ6RRi-bcDhI-djKO5uAwiyhfpbpYcaY_TxXWoCmq8N8uAw9zqFsQUwcXymfOAi2UF3eFZt02hBu-shKA&post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A7017%2Findex.html
注意
您可使用IdentityModel客户端库以编程方式建立end_session请求.NET代码。有关更多信息,请查看IdentityModel 文档。
此类为身份资源建模。
Enabled
指示此资源是否已启用且能够请求。默认为true。
Name
身份资源的惟一名称。这是客户端将用于受权请求中的scope参数的值。
DisplayName
该值将用于例如赞成屏幕上。
Description
该值将用于例如赞成屏幕上。
Required
指定用户是否能够在赞成屏幕上取消选择范围(若是赞成屏幕要实现此类功能)。默认为false。
Emphasize
指定赞成屏幕是否会强调此范围(若是赞成屏幕要实现此类功能)。将此设置用于敏感或重要范围。默认为false。
ShowInDiscoveryDocument
指定此范围是否显示在发现文档中。默认为true。
UserClaims
应包含在身份令牌中的关联用户声明类型的列表。
此类为API资源建模。
Enabled
指示此资源是否已启用且能够请求。默认为true。
Name
API的惟一名称。此值用于内省身份验证,并将添加到传出访问令牌的受众。
DisplayName
该值能够在例如赞成屏幕上使用。
Description
该值能够在例如赞成屏幕上使用。
ApiSecrets
API密钥用于内省端点。API可使用API名称和密钥进行内省验证。
UserClaims
应包含在访问令牌中的关联用户声明类型的列表。
Scopes
API必须至少有一个范围。每一个范围能够有不一样的设置。
在简单的状况下,API只有一个范围。可是在某些状况下,您可能但愿细分API的功能,并让不一样的客户端访问不一样的部分。
Name
范围的惟一名称。这是客户端将用于受权/令牌请求中的scope参数的值。
DisplayName
该值能够在例如赞成屏幕上使用。
Description
该值能够在例如赞成屏幕上使用。
Required
指定用户是否能够在赞成屏幕上取消选择范围(若是赞成屏幕要实现此类功能)。默认为false。
Emphasize
指定赞成屏幕是否会强调此范围(若是赞成屏幕要实现此类功能)。将此设置用于敏感或重要范围。默认为false。
ShowInDiscoveryDocument
指定此范围是否显示在发现文档中。默认为true。
UserClaims
应包含在访问令牌中的关联用户声明类型的列表。此处指定的声明将添加到为API指定的声明列表中。
只是关于为ApiResource
类提供的构造函数的注释。
要彻底控制数据ApiResource
,请使用不带参数的默认构造函数。若是要为每一个API配置多个范围,可使用此方法。例如:
new ApiResource
{
Name = "api2",
Scopes =
{
new Scope()
{
Name = "api2.full_access",
DisplayName = "Full access to API 2"
},
new Scope
{
Name = "api2.read_only",
DisplayName = "Read only access to API 2"
}
}
}
对于每一个API只须要一个范围的简单方案,则提供了几个接受a的便捷构造函数name
。例如:
new ApiResource("api1", "Some API 1")
使用便捷构造函数等同于:
new ApiResource
{
Name = "api1",
DisplayName = "Some API 1",
Scopes =
{
new Scope()
{
Name = "api1",
DisplayName = "Some API 1"
}
}
}
该Client
级车型的ID链接或OAuth 2.0用户端-例如,本地应用,Web应用程序或基于JS的应用程序。
Enabled
指定是否启用客户端。默认为true。
ClientId
客户端的惟一ID
ClientSecrets
客户端机密列表 - 访问令牌端点的凭据。
RequireClientSecret
指定此客户端是否须要密钥才能从令牌端点请求令牌(默认为true
)
AllowedGrantTypes
指定容许客户端使用的受权类型。将该GrantTypes
类用于常见组合。
RequirePkce
指定使用基于受权代码的受权类型的客户端是否必须发送校验密钥
AllowPlainTextPkce
指定使用PKCE的客户端是否可使用纯文本代码质询(不推荐 - 默认为false
)
RedirectUris
指定容许的URI以返回令牌或受权码
AllowedScopes
默认状况下,客户端无权访问任何资源 - 经过添加相应的范围名称来指定容许的资源
AllowOfflineAccess
指定此客户端是否能够请求刷新令牌(请求offline_access
范围)
AllowAccessTokensViaBrowser
指定是否容许此客户端经过浏览器接收访问令牌。这对于强化容许多种响应类型的流是有用的(例如,经过禁止混合流客户端,该客户端应该使用代码id_token来添加令牌响应类型,从而将令牌泄露给浏览器。
Properties
字典可根据须要保存任何自定义客户端特定值。
PostLogoutRedirectUris
指定在注销后重定向到的容许URI。有关更多详细信息,请参阅OIDC Connect会话管理规范。
FrontChannelLogoutUri
指定客户端的注销URI,以用于基于HTTP的前端通道注销。有关详细信息,请参阅OIDC Front-Channel规范。
FrontChannelLogoutSessionRequired
指定是否应将用户的会话ID发送到FrontChannelLogoutUri。默认为true。
BackChannelLogoutUri
指定客户端的注销URI,以用于基于HTTP的反向通道注销。有关详细信息,请参阅OIDC Back-Channel规范。
BackChannelLogoutSessionRequired
指定是否应在请求中将用户的会话ID发送到BackChannelLogoutUri。默认为true。
EnableLocalLogin
指定此客户端是否能够仅使用本地账户或外部IdP。默认为true。
IdentityProviderRestrictions
指定能够与此客户端一块儿使用的外部IdP(若是列表为空,则容许全部IdP)。默认为空。
IdentityTokenLifetime
标识令牌的生命周期(以秒为单位)(默认为300秒/ 5分钟)
AccessTokenLifetime
访问令牌的生命周期(以秒为单位)(默认为3600秒/ 1小时)
AuthorizationCodeLifetime
受权代码的生命周期(以秒为单位)(默认为300秒/ 5分钟)
AbsoluteRefreshTokenLifetime
刷新令牌的最长生命周期,以秒为单位。默认为2592000秒/ 30天
SlidingRefreshTokenLifetime
刷新令牌的生命周期以秒为单位。默认为1296000秒/ 15天
RefreshTokenUsage
ReUse
刷新令牌时刷新令牌句柄将保持不变OneTime
刷新令牌时将更新刷新令牌句柄。这是默认值。
RefreshTokenExpiration
Absolute
刷新令牌将在固定时间点到期(由AbsoluteRefreshTokenLifetime指定)Sliding
刷新令牌时,将刷新刷新令牌的生命周期(按SlidingRefreshTokenLifetime中指定的数量)。生命周期不会超过AbsoluteRefreshTokenLifetime。
UpdateAccessTokenClaimsOnRefresh
获取或设置一个值,该值指示是否应在刷新令牌请求上更新访问令牌(及其声明)。
AccessTokenType
指定访问令牌是引用令牌仍是自包含JWT令牌(默认为Jwt)。
IncludeJwtId
指定JWT访问令牌是否应具备嵌入的惟一ID(经过jti声明)。
AllowedCorsOrigins
若是指定,将由默认CORS策略服务实现(内存和EF)用于为JavaScript客户端构建CORS策略。
Claims
容许客户端的设置声明(将包含在访问令牌中)。
AlwaysSendClientClaims
若是设置,将为每一个流发送客户端声明。若是不是,仅用于客户端凭证流(默认为false)
AlwaysIncludeUserClaimsInIdToken
在请求id令牌和访问令牌时,若是用户声明始终将其添加到id令牌而不是请求客户端使用userinfo端点。默认值为false。
ClientClaimsPrefix
若是设置,前缀客户端声明类型将之前缀为。默认为client_。目的是确保它们不会意外地与用户声明冲突。
PairWiseSubjectSalt
对于此客户端的用户,在成对的subjectId生成中使用的salt值。
RequireConsent
指定是否须要赞成屏幕。默认为true。
AllowRememberConsent
指定用户是否能够选择存储赞成决策。默认为true。
ConsentLifetime
用户赞成的生命周期,以秒为单位。默认为null(无到期)。
ClientName
客户端显示名称(用于记录和赞成屏幕)
ClientUri
有关客户端的更多信息的URI(在赞成屏幕上使用)
LogoUri
URI到客户端徽标(在赞成屏幕上使用)
该GrantValidationResult
级车型补助确认为扩展赠款和资源全部者密码受权的结果。
最多见的用法是使用身份(成功案例)新建它:
context.Result = new GrantValidationResult(
subject: "818727",
authenticationMethod: "custom",
claims: optionalClaims);
...或使用错误和描述(失败案例):
context.Result = new GrantValidationResult(
TokenRequestErrors.InvalidGrant,
"invalid custom credential");
在这两种状况下,您均可以传递将包含在令牌响应中的其余自定义值。
IdentityServer一般在建立令牌或处理对userinfo或内省端点的请求时须要有关用户的身份信息。默认状况下,IdentityServer仅在身份验证cookie中具备声明,以便为此身份数据进行绘制。
将用户所需的全部可能声明放入cookie中是不切实际的,所以IdentityServer定义了一个扩展点,容许根据用户须要动态加载声明。这个可扩展点是IProfileService
开发人员一般能够实现此接口来访问包含用户身份数据的自定义数据库或API。
GetProfileDataAsync
预期为用户加载声明的API。它传递了一个实例ProfileDataRequestContext
。
IsActiveAsync
预期用于指示当前是否容许用户获取令牌的API。它传递了一个实例IsActiveContext
。
模拟用户声明的请求,而且是返回这些声明的工具。它包含如下属性:
Subject
该ClaimsPrincipal
模型的用户。
Client
的Client
用于正被请求的权利要求。
RequestedClaimTypes
请求的索赔类型集合。
Caller
正在请求声明的上下文的标识符(例如,身份令牌,访问令牌或用户信息端点)。常量IdentityServerConstants.ProfileDataCallers
包含不一样的常量值。
IssuedClaims
Claim
将返回的s 列表。预计这将由自定义IProfileService
实现填充。
AddRequestedClaims
扩展方法对ProfileDataRequestContext
填充IssuedClaims
,但首先过滤基于的声明RequestedClaimTypes
。
客户端请求的范围控制用户声明在令牌中返回给客户端的内容。该GetProfileDataAsync
方法负责根据上的RequestedClaimTypes
集合动态获取这些声明ProfileDataRequestContext
。
该RequestedClaimTypes
集合是基于该定义的用户索赔人口资源的范围进行建模。若是请求的范围是身份资源,则将RequestedClaimTypes
根据在中定义的用户声明类型填充声明中的声明IdentityResource
。若是请求的范围是API资源,则将RequestedClaimTypes
根据ApiResource
和/或中定义的用户声明类型填充声明中的声明Scope
。
对要肯定的请求建模是当前容许用户获取令牌。它包含如下属性:
Subject
该ClaimsPrincipal
模型的用户。
Client
的Client
用于正被请求的权利要求。
Caller
正在请求声明的上下文的标识符(例如,身份令牌,访问令牌或用户信息端点)。常量IdentityServerConstants.ProfileDataCallers
包含不一样的常量值。
IsActive
指示是否容许用户获取令牌的标志。预计这将由自定义IProfileService
实现分配。
该IIdentityServerInteractionService
接口旨在提供用户界面用于与IdentityServer通讯的服务,主要涉及用户交互。它能够从依赖注入系统得到,一般做为构造函数参数注入到IdentityServer的用户界面的MVC控制器中。
GetAuthorizationContextAsync
返回AuthorizationRequest
基于returnUrl
传递给登陆或赞成页面。
IsValidReturnUrl
指示returnUrl
登陆或赞成后是否为重定向的有效URL。
GetErrorContextAsync
返回ErrorMessage
基于errorId
传递给错误页面。
GetLogoutContextAsync
返回LogoutRequest
基于logoutId
传递给注销页面。
CreateLogoutContextAsync
logoutId
若是目前没有一个用于建立。这将建立一个cookie,捕获注销所需的全部当前状态,并logoutId
标识该cookie。这一般在没有当前时使用,logoutId
而且注销页面必须捕获当前用户在重定向到外部身份提供程序以进行注销以前注销所需的状态。新建立的logoutId
须要在注销时往返外部身份提供商,而后在注销回调页面上使用,就像在普通注销页面上同样。
GrantConsentAsync
接受a ConsentResponse
以通知IdentityServer用户赞成某个特定用户AuthorizationRequest
。
GetAllUserConsentsAsync
返回Consent
用户的集合。
RevokeUserConsentAsync
撤消用户对用户的全部赞成和受权。
RevokeTokensForCurrentSessionAsync
撤消用户在当前会话期间签署的客户的全部赞成和受权。
ClientId
发起请求的客户端标识符。
RedirectUri
成功受权后将用户重定向到的URI。
DisplayMode
显示模式从受权请求传递。
UiLocales
从受权请求传递的UI语言环境。
IdP
外部身份提供者请求。这用于绕过家庭领域发现(HRD)。这是经过“idp:”前缀提供给acr_values
受权请求的参数。
Tenant
租客要求。这是经过“tenant:”前缀提供给acr_values
受权请求的参数。
LoginHint
用户将用于登陆的预期用户名。这是经过login_hint
受权请求上的参数从客户端请求的。
PromptMode
从受权请求请求的提示模式。
AcrValues
从受权请求传递的acr值。
ScopesRequested
受权请求中请求的范围。
Parameters
整个参数集合传递给受权请求。
DisplayMode
显示模式从受权请求传递。
UiLocales
从受权请求传递的UI语言环境。
Error
错误代码。
RequestId
每请求标识符。这可用于向最终用户显示,并可用于诊断。
ClientId
发起请求的客户端标识符。
PostLogoutRedirectUri
用户在注销后将其重定向到的URL。
SessionId
用户当前的会话ID。
SignOutIFrameUrl
要在<iframe>
注销页面上呈现以启用单点注销的URL 。
Parameters
整个参数集合传递给结束会话端点。
ShowSignoutPrompt
指示是否应根据传递到结束会话端点的参数提示用户注销。
ScopesConsented
用户赞成的范围集合。
RememberConsent
指示是否持久保留用户赞成的标志。
SubjectId
授予赞成的主题ID。
ClientId
赞成的客户端标识符。
Scopes
范围的集合赞成。
CreationTime
得到赞成的日期和时间。
Expiration
赞成过时的日期和时间。
IssuerUri
设置将在发现文档和已颁发的JWT令牌中显示的颁发者名称。建议不要设置此属性,该属性从客户端使用的主机名中推断颁发者名称。
PublicOrigin
此服务器实例的来源,例如https://myorigin.com。若是未设置,则从请求推断出原始名称。
容许启用/禁用各个端点,例如令牌,受权,用户信息等。
默认状况下,全部端点都已启用,但您能够经过禁用不须要的端点来锁定服务器。
容许启用/禁用发现文档的各个部分,例如端点,范围,声明,受权类型等。
该CustomEntries
字典容许向发现文档添加自定义元素。
CookieLifetime
身份验证cookie生存期(仅在使用IdentityServer提供的cookie处理程序时有效)。
CookieSlidingExpiration
指定cookie是否应该滑动(仅在使用IdentityServer提供的cookie处理程序时有效)。
RequireAuthenticatedUserForSignOutMessage
指示是否必须对用户进行身份验证以接受结束会话端点的参数。默认为false。
CheckSessionCookieName
用于检查会话端点的cookie的名称。
RequireCspFrameSrcForSignout
若是设置,将要求frame-src CSP标头在结束会话回调端点上发出,该端点向客户端呈现iframe以进行前端通道注销通知。默认为true。
容许配置是否应将哪些事件提交到已注册的事件接收器。有关活动的更多信息,请参见此处。
容许设置各类协议参数的长度限制,如客户端ID,范围,重定向URI等。
LoginUrl
,LogoutUrl
,ConsentUrl
,ErrorUrl
设置登陆,注销,赞成和错误页面的URL。
LoginReturnUrlParameter
设置传递给登陆页面的返回URL参数的名称。默认为returnUrl。
LogoutIdParameter
设置传递给注销页面的注销消息id参数的名称。默认为logoutId。
ConsentReturnUrlParameter
设置传递给赞成页面的返回URL参数的名称。默认为returnUrl。
ErrorIdParameter
设置传递给错误页面的错误消息id参数的名称。默认为errorId。
CustomRedirectReturnUrlParameter
设置从受权端点传递给自定义重定向的返回URL参数的名称。默认为returnUrl。
CookieMessageThreshold
IdentityServer和某些UI页面之间的某些交互须要cookie来传递状态和上下文(上面的任何具备可配置“message id”参数的页面)。因为浏览器对cookie的数量及其大小有限制,所以该设置用于防止建立过多的cookie。该值设置将建立的任何类型的消息cookie的最大数量。一旦达到限制,将清除最先的消息cookie。这有效地表示用户在使用IdentityServer时能够打开多少个选项卡。
这些设置仅在启动时在服务配置中启用了相应的缓存时才适用。
ClientStoreExpiration
从客户端存储加载的客户端配置的缓存持续时间。
ResourceStoreExpiration
缓存从资源存储加载的标识和API资源配置的持续时间。
IdentityServer支持某些端点的CORS。底层CORS实现由ASP.NET Core提供,所以它在依赖注入系统中自动注册。
CorsPolicyName
将为IdentityServer中的CORS请求评估的CORS策略的名称(默认为"IdentityServer4"
)。处理此问题的策略提供程序是根据ICorsPolicyService
依赖注入系统中注册的方式实现的。若是您但愿自定义容许链接的CORS源集,那么建议您提供自定义实现ICorsPolicyService
。
CorsPaths
IdentityServer中支持CORS的端点。默认为发现,用户信息,令牌和吊销端点。
PreflightCacheDuration
Nullable <TimeSpan>指示在预检Access-Control-Max-Age响应头中使用的值。默认为null,表示未在响应上设置缓存标头。
在适当的状况下,IdentityServer会为某些响应发出CSP标头。
Level
要使用的CSP级别。默认状况下使用CSP级别2,但若是必须支持旧浏览器,则将其更改CspLevel.One
为容纳它们。
AddDeprecatedHeader
指示是否X-Content-Security-Policy
还应发出较旧的CSP标头(除了基于标准的标头值)。默认为true。
为IdentityServer中的配置和操做数据扩展点提供了基于EntityFramework的实现。EntityFramework的使用容许任何EF支持的数据库与此库一块儿使用。
此库提供的功能分为两个主要区域:配置存储和操做存储支持。根据托管应用程序的须要,这两个不一样的区域能够独立使用或一块儿使用。
若是但愿从EF支持的数据库加载客户端,标识资源,API资源或CORS数据(而不是使用内存配置),则可使用配置存储。此支持提供了IClientStore
和IResourceStore
,以及ICorsPolicyService
可扩展性点的实现。这些实现使用一个DbContext
被调用的类ConfigurationDbContext
来为数据库中的表建模。
要使用配置存储支持,请AddConfigurationStore
在调用后使用扩展方法AddIdentityServer
:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer4.EntityFramework-2.0.0;trusted_connection=yes;";
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddIdentityServer()
// this adds the config data from DB (clients, resources, CORS)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
});
}
要配置配置存储,请使用ConfigurationStoreOptions
传递给配置回调的options对象。
此选项类包含用于控制配置存储的属性ConfigurationDbContext
。
ConfigureDbContext
Action<DbContextOptionsBuilder>
用做回调的类型委托来配置底层证券ConfigurationDbContext
。ConfigurationDbContext
若是直接使用EF,代理能够以相同的方式配置AddDbContext
,这容许使用任何EF支持的数据库。
DefaultSchema
容许为中的全部表设置默认数据库模式名称ConfigurationDbContext
。
若是但愿从EF支持的数据库(而不是默认的内存数据库)加载受权授予,赞成和令牌(刷新和引用),则可使用操做存储。此支持提供了IPersistedGrantStore
可扩展性点的实现。该实现使用一个DbContext
被调用的类PersistedGrantDbContext
来为数据库中的表建模。
要使用操做商店支持,请AddOperationalStore
在调用后使用扩展方法AddIdentityServer
:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer4.EntityFramework-2.0.0;trusted_connection=yes;";
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddIdentityServer()
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30; // interval in seconds
});
}
要配置操做存储,请使用OperationalStoreOptions
传递给配置回调的options对象。
此选项类包含用于控制操做存储的属性PersistedGrantDbContext
。
ConfigureDbContext
Action<DbContextOptionsBuilder>
用做回调的类型委托来配置底层证券PersistedGrantDbContext
。PersistedGrantDbContext
若是直接使用EF,代理能够以相同的方式配置AddDbContext
,这容许使用任何EF支持的数据库。
DefaultSchema
容许为中的全部表设置默认数据库模式名称PersistedGrantDbContext
。
EnableTokenCleanup
指示是否将从数据库中自动清除过期条目。默认是false
。
TokenCleanupInterval
令牌清理间隔(以秒为单位)。默认值为3600(1小时)。
跨不一样版本的IdentityServer(以及EF支持)极可能会更改数据库架构以适应新的和不断变化的功能。
咱们不为建立数据库或将数据从一个版本迁移到另外一个版本提供任何支持。您须要以组织认为合适的任何方式管理数据库建立,架构更改和数据迁移。
使用EF迁移是一种可行的方法。若是您确实但愿使用迁移,请参阅EF快速入门以获取有关如何入门的示例,或参阅有关EF迁移的Microsoft 文档。
咱们还为当前版本的数据库模式发布了示例SQL脚本。
提供了基于ASP.NET身份的实现,用于管理IdentityServer用户的身份数据库。此实现实现IdentityServer中的扩展点,以便为用户加载身份数据以将声明发送到令牌。
要使用此库,请正常配置ASP.NET标识。而后AddAspNetIdentity
在调用后使用扩展方法AddIdentityServer
:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddIdentityServer()
.AddAspNetIdentity<ApplicationUser>();
}
AddAspNetIdentity
须要做为通用参数来为您的用户建模ASP.NET身份的类(以及传递给AddIdentity
ASP.NET身份的同一个用户)。这将配置IdentityServer使用的ASP.NET身份的实现IUserClaimsPrincipalFactory
,IResourceOwnerPasswordValidator
和IProfileService
。它还配置了一些用于IdentityServer的ASP.NET Identity选项(例如要使用的声明类型和身份验证cookie设置)。
如下是一些在线,远程和课堂培训选项,以了解有关ASP.NET核心身份和IdentityServer4的更多信息。
这是咱们为期三天的旗舰课程(包括普遍的实践实验室),咱们做为会议,现场和远程的一部分提供。
可在此处找到公共培训的议程和日期,请 联系咱们参加私人研讨会。
PluralSight有一些关于身份,ASP.NET Core和IdentityServer的好课程。
新
旧的
[17/01 NDC London] - ASP.NET Core v2上的IdentityServer v2 - 一个更新
[17/01 DotNetRocks] - DotNetRocks上的IdentityServer和PolicyServer
[14/09 Microsoft Learning] - IdentityServer for ASP.NET Core简介 - Brock Allen
[14/06 NDC Oslo] - 实施Web应用程序和API的受权
[22/02 NDC Mini Copenhagen] - IdentityServer4:ASP.NET核心的新增和改进 - Dominick Baier