本文已更新到 .NET Core 2.2
本文包括后续的Demo都会放在github:https://github.com/stulzq/IdentityServer4.Samples (QuickStart的几个Demo随着本系列的更新,目前为从官方Demo仓库的拷贝,防止本文和Demo不匹配,由于官方Demo和文档一直在更新,本系列更新速度可能会慢一步)。
这里特别说明一下:快速入门以及Topic系列为了保持到最新,目前几乎都是翻译的官方文档(以往的不适合最新版本就换掉了),须要深刻一点的请看实战系列。git
此示例介绍了使用IdentityServer保护API的最基本场景。github
在这种状况下,咱们将定义一个API和要访问它的客户端。 客户端将在IdentityServer上请求访问令牌,并使用它来访问API。api
建立一个名为QuickstartIdentityServer
的ASP.NET Core Web 空项目(asp.net core 2.2),端口5000
建立一个名为Api
的ASP.NET Core Web Api 项目(asp.net core 2.2),端口5001
建立一个名为Client
的控制台项目(.net core 2.2)浏览器
在QuickstartIdentityServer
项目中添加一个Config.cs
文件:app
public static class Config { public static IEnumerable<IdentityResource> GetIdentityResources() { return new IdentityResource[] { new IdentityResources.OpenId() }; } public static IEnumerable<ApiResource> GetApis() { return new List<ApiResource> { new ApiResource("api1", "My API") }; } 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的客户端模式进行身份验证。 将如下代码添加到Config.cs
文件中:asp.net
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以使用Scope和客户端定义,您须要向ConfigureServices方法添加代码。ide
Startup.cs
post
public void ConfigureServices(IServiceCollection services) { var builder = services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApis()) .AddInMemoryClients(Config.GetClients()); // rest omitted } public void Configure(IApplicationBuilder app) { if (Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // uncomment if you want to support static files //app.UseStaticFiles(); app.UseIdentityServer(); // uncomment, if you wan to add an MVC-based UI //app.UseMvcWithDefaultRoute(); }
运行此项目,打开浏览器访问http://localhost:5000/.well-known/openid-configuration
你将会看到IdentityServer的各类元数据信息。ui
首次启动时,IdentityServer将为您建立一个开发人员签名密钥,它是一个名为tempkey.rsa的文件。 您没必要将该文件检入源代码管理中,若是该文件不存在,将从新建立该文件。.net
在项目Api
中添加一个Controller:IdentityController
[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 }); } }
最后一步是将身份验证服务添加到DI和身份验证中间件到管道。 这些将:
将Startup更新为以下所示:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvcCore() .AddAuthorization() .AddJsonFormatters(); services.AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = "http://localhost:5000"; options.RequireHttpsMetadata = false; options.Audience = "api1"; }); } public void Configure(IApplicationBuilder app) { app.UseAuthentication(); app.UseMvc(); } }
AddAuthentication
将身份认证服务添加到DI,并将“Bearer”配置为默认方案。 AddJwtBearer
将 JWT 认证处理程序添加到DI中以供身份认证服务使用。 UseAuthentication
将身份认证中间件添加到管道中,所以将在每次调用API时自动执行身份验证。
若是在浏览器访问(http:// localhost:5001/identity),你会获得HTTP 401的结果。 这意味着您的API须要凭据。
就是这样,API如今受 IdentityServer 保护。
为 "Client" 项目添加 Nuget 包:IdentityModel
IdentityModel 包括用于发现 IdentityServer 各个终结点(EndPoint)的客户端库。这样您只须要知道 IdentityServer 的地址 - 能够从元数据中读取实际的各个终结点地址:
// discover endpoints from metadata var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000"); if (disco.IsError) { Console.WriteLine(disco.Error); return; }
DiscoveryClient 已在最新版移除
接下来,您可使用从 IdentityServer 元数据获取到的Token终结点请求令牌:
// request token var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = disco.TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1" }); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } Console.WriteLine(tokenResponse.Json);
要将Token发送到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)); }
输出应以下所示:
默认状况下,Token将包含有关 Scope,生命周期(nbf和exp),客户端ID(client_id)和颁发者名称(iss)的身份信息单元(Claim)。
github地址: https://github.com/stulzq/IdentityServer4.Samples/tree/master/Quickstarts/1_ClientCredentials