IdentityServer4 入门之一/之二

IdentityServer4 入门之一/之二IdentityServ 快速入门 官网示例基于 NETCore2 0 这里使用 NETCore2 1 重新实现 IdentityServ 快速入门之一 IdentityServ 官网示例地址 https github com IdentityServ IdentityServ Samples tree release Quickstarts 1 ClientCreden 此示例使用 NETCore2 0 开发 我们这里升级为 NETCor

IdentityServer4 快速入门,官网示例基于 .NET Core 2.0,这里使用 .NET Core 2.1 重新实现。

IdentityServer4 快速入门之一

IdentityServer4 官网示例地址:https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/Quickstarts/1_ClientCredentials。
此示例使用 .NET Core 2.0 开发,我们这里升级为 .NET Core 2.1 版本。

快速构建一个基本的 API 服务

我们快速构建一个基本的 API 服务器,后继我们会使用 IdentityServer4 保护起来,但是,在开始的时候,它是可以直接访问的。
该服务提供一个访问端点 http://localhost:5010/identity,其可以返回当前访问用户的 Claim

创建空的网站项目

使用 dotnet CLI 快速创建基本的 Web 项目。

dotnet new web -o apiServer 
添加 MVC 服务支持

打开 Startup.cs ,添加 MVC 服务支持。

ConfigureServices 方法中添加对 MVC 的支持

services.AddMvc(); 

Configure() 方法中添加对 MVC 服务的配置。

app.UseMvc(routes => { 
    routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); 

完整的 Startup.cs 文件如下:

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; namespace apiServer { 
    public class Startup { 
    // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID= public void ConfigureServices(IServiceCollection services) { 
    services.AddMvc(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { 
    if (env.IsDevelopment()) { 
    app.UseDeveloperExceptionPage(); } app.UseMvc(routes => { 
    routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } } 
自定义服务器端口

默认情况下,ASP.NET Core 的 Web 应用将会使用 5000 端口,我们希望将该服务器的端口修改为 5010,以便让开验证服务器的端口。

Program.cs 修改为如下所示:

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace apiServer { 
    public class Program { 
    public static void Main(string[] args) { 
    CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => /* WebHost.CreateDefaultBuilder(args) .UseStartup 
   
     (); */ 
    new WebHostBuilder() .UseKestrel() .UseUrls("http://localhost:5010") .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>(); } } 
添加 API 控制器

在项目根目录下,创建 Controllers 文件夹,这是默认的控制器文件夹。在其中创建 IdentityController.cs 控制器文件。

该控制器将获取用户的 Claim 并以 JSON 形式返回。

using System.Linq; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; [Route("identity")] public class IdentityController : ControllerBase { 
    [HttpGet()] public IActionResult Get() { 
    return new JsonResult(from c in User.Claims select new { 
    c.Type, c.Value }); } } 
启动并访问该服务

执行 dotnetrun 命令,启动服务。

dotnet run 

在浏览器中输入地址 http://localhost:5010/identity 以访问该服务,应该返回以下结果。

[] 
保护该服务
使用 [Authorize] 特性包含控制器

在控制器上使用 [Authorize] 特性保护控制器,这样,在用户没有得到授权的情况下,将不能访问该控制器。

修改之后的控制器代码如下:

using System.Linq; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; [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 }); } } 

由于并没有设置验证的模式,再次访问 http://localhost:5010/identity,将会得到如下错误信息。

An unhandled exception occurred while processing the request. InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found. Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties) 
添加 IdentityServer4 验证服务

在项目中添加对于 IdentityServer4AccessTokenValidation 支持。使用 dotnetCLI 工具添加。

dotnet add package IdentityServer4.AccessTokenValidatio 

添加之后的项目文件内容:

<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.1 
     TargetFramework>  
      PropertyGroup> <ItemGroup> <Folder Include="wwwroot\" />  
       ItemGroup> <ItemGroup> <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.6.0" /> <PackageReference Include="Microsoft.AspNetCore.App" />  
        ItemGroup>  
         Project> 
使用 IdentityServer4 的验证服务

Startup.cs 文件的 ConfigureServices 方法中,在 MVC 服务之前,添加验证和授权服务支持。

services .AddAuthentication(options => { 
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(o => { 
    o.Authority = "http://localhost:5000"; o.Audience = "api1"; o.RequireHttpsMetadata = false; }); 

完整的 Startup.cs 内容

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using IdentityServer4.AccessTokenValidation; using Microsoft.AspNetCore.Authentication.JwtBearer; namespace apiServer { 
    public class Startup { 
    // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID= public void ConfigureServices(IServiceCollection services) { 
    services .AddAuthentication(options => { 
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(o => { 
    o.Authority = "http://localhost:5000"; o.Audience = "api1"; o.RequireHttpsMetadata = false; }); services.AddMvc(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { 
    if (env.IsDevelopment()) { 
    app.UseDeveloperExceptionPage(); } app.UseAuthentication(); app.UseMvc(routes => { 
    routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } } 
检查受保护的服务

此时,重新访问 http://localhost:5010/identity,由于没有提供 access token,服务器会返回 401 错误。

HTTP ERROR 401 

实现 OAuth2 授权服务器

使用 IdentityServer4 创建授权服务器。

创建项目

使用 dotnet 的 CLI 工具创建项目,仍然是一个 Web 项目。

dotnet new web -o Starter 

然后,添加 IdentityServer4 包。

dotnet add package IdentityServer4 

添加之后的项目文件内容:

<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.1 
     TargetFramework>  
      PropertyGroup> <ItemGroup> <Folder Include="wwwroot\" />  
       ItemGroup> <ItemGroup> <PackageReference Include="IdentityServer4" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.App" />  
        ItemGroup>  
         Project> 
配置参数

我们在代码中配置授权服务器支持的用户和 scope

创建 Config.cs 文件,在两个静态方法中分别提供 API 资源和用户。

我们创建的用户标识为 client,密钥为 secret。

using IdentityServer4.Models; using System.Collections.Generic; namespace QuickstartIdentityServer { 
    public class Config { 
    // scopes define the API resources in your system public static IEnumerable<ApiResource> GetApiResources() { 
    return new List<ApiResource> { 
    new ApiResource("api1", "My API") }; } // clients want to access resources (aka scopes) public static IEnumerable<Client> GetClients() { 
    // 唯一的一个用户 return new List<Client> { 
    new Client { 
    ClientId = "client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { 
    new Secret("secret".Sha256()) }, AllowedScopes = { 
    "api1" } } }; } } } 
配置服务

Startup.cs 中配置 IdentityServer4 服务。

  • ConfigureServices 中设置对 IdentityServer 服务的支持
  • 在 Configure 中使用默认配置。
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using QuickstartIdentityServer; namespace starter { 
    public class Startup { 
    // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID= public void ConfigureServices(IServiceCollection services) { 
    services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { 
    if (env.IsDevelopment()) { 
    app.UseDeveloperExceptionPage(); } app.UseIdentityServer(); } } } 
运行服务

使用 dotnet 命令启动服务

dotnet run 

应该看到如下输出

>dotnet run Using launch settings from C:\study\dotnetcore\identityserver4\starter\Properties\launchSettings.json... info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] User profile is available. Using 'C:\Users\xxx\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest. info: IdentityServer4.Startup[0] You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation. Hosting environment: Development Content root path: C:\study\dotnetcore\identityserver4\starter Now listening on: https://localhost:5001 Now listening on: http://localhost:5000 Application started. Press Ctrl+C to shut down. 

我们不会直接使用这个服务器,在 API 服务器的配置中,我们使用了

o.Authority = "http://localhost:5000"; 

来配置授权服务器。

创建消费服务的客户端

IdentityModel

我们创建一个控制台应用来消费 API 服务,使用 IdentityModel 辅助访问 OAuth2.0 的服务端。

TokenClient

Client library for OAuth 2.0 and OpenID Connect token endpoints.

Features:

  • Support for client credentials & resource owner password credential flow
  • Support for exchanging authorization codes with tokens
  • Support for refreshing refresh tokens
  • Support for client credentials via Basic Authentication, POST body and X.509 client certificates
  • Extensible for custom parameters
  • Parsing of token response messages

Example:

var client = new TokenClient( "https://server/token", "client_id", "secret"); var response = await client.RequestClientCredentialsAsync("scope"); var token = response.AccessToken; 
IdentityModel2

新的 IdentityModel2 项目提供了更佳的支持。

创建客户端项目

使用 dotnet 的命令行工具创建控制台项目

dotnet new console -o Client 

添加对 IdentityModel 的引用。

dotnet add package IdentityModel 

添加之后的项目文件如下所示:

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe 
     OutputType> <TargetFramework>netcoreapp2.1 
      TargetFramework>  
       PropertyGroup> <ItemGroup> <PackageReference Include="IdentityModel" Version="3.9.0" />  
        ItemGroup>  
         Project> 
访问 API 服务
使用 IdentityModel 方式访问

首先我们使用元数据来发现服务端点。

var disco = await DiscoveryClient.GetAsync("http://localhost:5000"); 

然后,我们直接创建了用户的 Token,以访问资源。

var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret"); 

随后,我们获取 access token

var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1"); 

最终,我们使用这个 access token 通过 HttpClient 访问服务器资源。

var client = new HttpClient(); client.SetBearerToken(tokenResponse.AccessToken); var response = await client.GetAsync("http://localhost:5010/identity"); 

完整代码如下所示:

using IdentityModel.Client; using Newtonsoft.Json.Linq; using System; using System.Net.Http; using System.Threading.Tasks; namespace Client { 
    public class Program { 
    public static void Main(string[] args) => MainAsync().GetAwaiter().GetResult(); private static async Task MainAsync() { 
    // discover endpoints from metadata var disco = await DiscoveryClient.GetAsync("http://localhost:5000"); if (disco.IsError) { 
    Console.WriteLine(disco.Error); return; } // 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("Token:"); Console.WriteLine(tokenResponse.Json); Console.WriteLine("\n\n"); // call api var client = new HttpClient(); client.SetBearerToken(tokenResponse.AccessToken); var response = await client.GetAsync("http://localhost:5010/identity"); if (!response.IsSuccessStatusCode) { 
    Console.WriteLine(response.StatusCode); } else { 
    var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); } } } } 
使用 IdentityModel2 语法访问
发现服务端点
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000"); 
请求 access token

注意 GrantType"client_credentials",或者使用常量 IdentityModel.OidcConstants.GrantTypes.ClientCredentials 来表示。

var tokenResponse = await client.RequestTokenAsync(new TokenRequest { 
    Address = disco.TokenEndpoint, GrantType = "client_credentials", ClientId = "client", ClientSecret = "secret", Parameters = { 
    { 
    "scope", "api1" } } }); 
使用 access token 访问服务
client.SetBearerToken(tokenResponse.AccessToken); var response = await client.GetAsync("http://localhost:5010/identity"); 

完整代码:

using IdentityModel.Client; using Newtonsoft.Json.Linq; using System; using System.Net.Http; using System.Threading.Tasks; namespace Client { 
    public class Program { 
    public static void Main(string[] args) => MainAsync().GetAwaiter().GetResult(); private static async Task MainAsync() { 
    var client = new HttpClient(); // discover endpoints from metadata var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000"); if (disco.IsError) { 
    Console.WriteLine(disco.Error); return; } // request token var tokenResponse = await client.RequestTokenAsync(new TokenRequest { 
    Address = disco.TokenEndpoint, GrantType = IdentityModel.OidcConstants.GrantTypes.ClientCredentials, ClientId = "client", ClientSecret = "secret", Parameters = { 
    { 
    "scope", "api1" } } }); Console.WriteLine("Token:"); Console.WriteLine(tokenResponse.Json); Console.WriteLine("\n\n"); // call api client.SetBearerToken(tokenResponse.AccessToken); var response = await client.GetAsync("http://localhost:5010/identity"); if (!response.IsSuccessStatusCode) { 
    Console.WriteLine(response.StatusCode); } else { 
    var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); } } } } 
访问服务

运行客户端,访问服务,可以看到如下输出:

>dotnet run Token: { 
    "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQ3OWUzMmE4ZWI5OWZjMDljMTdiZjI2NTI5NmZlNjMwIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1MzU2MTY0MTAsImV4cCI6MTUzNTYyMDAxMCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6NTAwMC9yZXNvdXJjZXMiLCJhcGkxIl0sImNsaWVudF9pZCI6ImNsaWVudCIsInNjb3BlIjpbImFwaTEiXX0.V6aOV7tX59coLEkMpM3p9l3ppydmpjEcupRhHKHAkJMkA1Pb8w_0splbMMJH4G_iHRhKA8ueJn6XtDW24pcM1cdaulM2wt63Yc1h5fuUUawkJeHPdgRNWUBY5d8h-RPUamNuMparw3X1YpLMGKX5tFB8j8wYmUb_mhPhYjsXqSkDxxIHGpkxGd4RSFGkMMH0iX-jOwXV_tR91naY7WXxJD-TGYXJn0MT2L13MGARMiTIAVpGksPfgE9dhtKGRQ2BFC45BewnHdJSsEhD6UBWhRce5248HejE7dW_gI6xRYZrfd2e3nKyyBPwT67AN26Wiuv4dixuCGUolrbZ3JgteA", "expires_in": 3600, "token_type": "Bearer" } [ { 
    "type": "nbf", "value": "" }, { 
    "type": "exp", "value": "" }, { 
    "type": "iss", "value": "http://localhost:5000" }, { 
    "type": "aud", "value": "http://localhost:5000/resources" }, { 
    "type": "aud", "value": "api1" }, { 
    "type": "client_id", "value": "client" }, { 
    "type": "scope", "value": "api1" } ] 

此时,检查 IdentityServer 服务器的输出,可以看到如下内容:

>dotnet run Using launch settings from C:\study\dotnetcore\identityserver4\starter\Properties\launchSettings.json... info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] User profile is available. Using 'C:\Users\xxx\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest. info: IdentityServer4.Startup[0] You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation. Hosting environment: Development Content root path: C:\study\dotnetcore\identityserver4\starter Now listening on: https://localhost:5001 Now listening on: http://localhost:5000 Application started. Press Ctrl+C to shut down. info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http://localhost:5000/.well-known/openid-configuration info: IdentityServer4.Hosting.IdentityServerMiddleware[0] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 148.0893ms 200 application/json; charset=UTF-8 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http://localhost:5000/.well-known/openid-configuration/jwks info: IdentityServer4.Hosting.IdentityServerMiddleware[0] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryKeyEndpoint for /.well-known/openid-configuration/jwks info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 21.7819ms 200 application/json; charset=UTF-8 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 POST http://localhost:5000/connect/token application/x-www-form-urlencoded 40 info: IdentityServer4.Hosting.IdentityServerMiddleware[0] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.TokenEndpoint for /connect/token info: IdentityServer4.Validation.TokenRequestValidator[0] Token request validation success { 
    "ClientId": "client", "GrantType": "client_credentials", "Scopes": "api1", "Raw": { 
    "grant_type": "client_credentials", "scope": "api1" } } info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 210.8521ms 200 application/json; charset=UTF-8 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http://localhost:5000/.well-known/openid-configuration info: IdentityServer4.Hosting.IdentityServerMiddleware[0] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 0.7336ms 200 application/json; charset=UTF-8 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1] Request starting HTTP/1.1 GET http://localhost:5000/.well-known/openid-configuration/jwks info: IdentityServer4.Hosting.IdentityServerMiddleware[0] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryKeyEndpoint for /.well-known/openid-configuration/jwks info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 0.5583ms 200 application/json; charset=UTF-8 

IdentityServer4 入门之二:使用资源所有者密码授权保护 API

OAuth 2.0 资源所有者密码授权 允许一个客户端发送用户名和密码到令牌服务并获得一个表示该用户的访问令牌。

(OAuth 2.0) 规范 建议仅对“受信任”的应用程序使用资源所有者密码授权。一般来说,当你想要验证一个用户并请求访问令牌的时候,使用交互式 OpenID Connect 流通常会更好。

不过,这个授权类型允许我们在 IdentityServer 快速入门中引入 用户 的概念,这是我们要展示它的原因。

添加用户

就像基于内存存储的资源(即 范围 Scopes)和客户端一样,对于用户也可以这样做。

注意:查看基于 ASP.NET Identity 的快速入门以获得更多关于如何正确存储和管理用户账户的信息 

IdentityServer4.Test 命名空间下,IdentityServer4 预定义了用于测试的用户类 TestUser,在代码注释中,说明此内存中的用户对象用于测试,不要用于生产环境。

using System.Collections.Generic; using System.Security.Claims; using IdentityModel; namespace IdentityServer4.Test { 
     public class TestUser { 
     public string SubjectId { 
     get; set; } public string Username { 
     get; set; } public string Password { 
     get; set; } public string ProviderName { 
     get; set; } public string ProviderSubjectId { 
     get; set; } public bool IsActive { 
     get; set; } = true; public ICollection<Claim> Claims { 
     get; set; } = new HashSet<Claim> (new ClaimComparer ()); } } 

Config.cs 文件中的配置类中,添加获取用户的方法。

public static List<TestUser> GetUsers() { 
     return new List<TestUser>() { 
     new TestUser { 
     SubjectId="1", Username="爱丽丝", Password="password" }, new TestUser { 
     SubjectId="2", Username="博德", Password="password" } }; } 

然后,使用 AddTestUsers() 方法将测试用户注册到 IdentityServer 中。

public void ConfigureServices(IServiceCollection services) { 
     // 使用内存存储,密钥,客户端和资源来配置身份服务器。 services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()) .AddTestUsers(Config.GetUsers()); } 

AddTestUsers 扩展方法在背后做了以下几件事:

  • 为资源所有者密码授权添加支持
  • 添加对用户相关服务的支持,这服务通常为登录 UI 所使用(我们将在下一个快速入门中用到登录 UI)
  • 为基于测试用户的身份信息服务添加支持(你将在下一个快速入门中学习更多与之相关的东西)

AddTestUsers 的代码如下所示:

using System.Collections.Generic; using IdentityServer4.Test; namespace Microsoft.Extensions.DependencyInjection { 
     ///  /// Extension methods for the IdentityServer builder ///  public static class IdentityServerBuilderExtensions { 
     public static IIdentityServerBuilder AddTestUsers (this IIdentityServerBuilder builder, List<TestUser> users) { 
     builder.Services.AddSingleton (new TestUserStore (users)); builder.AddProfileService<TestUserProfileService> (); builder.AddResourceOwnerValidator<TestUserResourceOwnerPasswordValidator> (); return builder; } } } 

在生产环境下,你需要使用 AddResourceOwnerValidator 来注册自己实现的验证器。

此时的 TestUserprofileService 提供了对用户的 profile 的支持。

IdentityServer 中,实现资源所有者密码验证需要实现接口IResourceOwnerPasswordValidator, TestUserResourceOwnerPasswordValidator 实现该接口并完成实际的用户验证工作。

using System; using System.Threading.Tasks; using IdentityModel; using IdentityServer4.Validation; using Microsoft.AspNetCore.Authentication; namespace IdentityServer4.Test { 
     ///  /// Resource owner password validator for test users ///  /// 
     public class TestUserResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator { 
     private readonly TestUserStore _users; private readonly ISystemClock _clock; public TestUserResourceOwnerPasswordValidator (TestUserStore users, ISystemClock clock) { 
     _users = users; _clock = clock; } public Task ValidateAsync (ResourceOwnerPasswordValidationContext context) { 
     if (_users.ValidateCredentials (context.UserName, context.Password)) { 
     var user = _users.FindByUsername (context.UserName); context.Result = new GrantValidationResult ( user.SubjectId ?? throw new ArgumentException ("Subject ID not set", nameof (user.SubjectId)), OidcConstants.AuthenticationMethods.Password, _clock.UtcNow.UtcDateTime, user.Claims); } return Task.CompletedTask; } } } 

为资源所有者密码授权添加一个客户端定义

你可以通过修改 AllowedGrantTypes 属性简单地添加对已有客户端授权类型的支持。

通常你会想要为资源所有者用例创建独立的客户端,添加以下代码到你配置中的客户端定义中:

public static IEnumerable<Client> GetClients() { 
     return new List<Client> { 
     // 省略其他客户端定义... // 资源所有者密码授权客户端定义 new Client { 
     ClientId = "ro.client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { 
     new Secret("secret".Sha256()) }, AllowedScopes = { 
     "api1" } } }; } 

使用密码授权请求一个令牌

客户端看起来跟之前 客户端凭证授权 的客户端是相似的。主要差别在于现在的客户端将会以某种方式收集用户密码,然后在令牌请求期间发送到令牌服务。

IdentityModelTokenClient 在这里再次为我们提了供帮助:

// 请求以获得令牌 var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret"); var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("爱丽丝", "password", "api1"); if (tokenResponse.IsError) { 
     Console.WriteLine(tokenResponse.Error); return; } Console.WriteLine(tokenResponse.Json); Console.WriteLine("\n\n"); 

sub 信息的存在(或缺失)使得 API 能够区分代表客户端的调用和代表用户的调用。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/214384.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午4:23
下一篇 2026年3月18日 下午4:24


相关推荐

  • 普林斯顿结构与哈佛结构的相同之处_哈佛结构的特点

    普林斯顿结构与哈佛结构的相同之处_哈佛结构的特点   冯.诺依曼结构,又称为普林斯顿结构。是一种经典的体系结构,有CPU,总线,外部存储器组成。这种体系结构采用程序代码存储器与数据存储器合并在同一存储器里,但程序代码存储器地址与数据存储器地址分别指向不同的物理地址。程序指令宽度与数据宽度一样。数据总线和地址总线共用。   但是随着CPU设计的发展,pipeline的增加,指令和数据的互斥读取很影响CPU指令执行的scale程度。后来,哈…

    2022年10月4日
    4
  • SocketTimeoutException和ConnectException简介

    SocketTimeoutException和ConnectException简介随时随地阅读更多技术实战干货 获取项目源码 学习资料 请关注源代码社区公众号 ydmsq666 SocketTimeou 一 简介 SocketTimeou 指的是服务器响应超时直接继承自 java io InterruptedI 实现了可

    2026年3月26日
    1
  • java消息总线ibus_SpringCloud Bus 消息总线

    java消息总线ibus_SpringCloud Bus 消息总线一 能干嘛 二 什么是总线 三 动态刷新全局广播注意 必须先具备良好的 RabbitMQ 环境演示广播效果 增加复杂度 再以 3355 为模板再制作一个 33661 新建 cloud config client 33661 POMcloud2020 atguigu springcloud1 0 SNAPSHOT4 0 0cloud config client org springframew

    2026年3月16日
    2
  • 网络安全攻击防御体系

    网络安全攻击防御体系nbsp nbsp

    2025年11月13日
    5
  • 浅谈VMware的NAT模式「建议收藏」

    浅谈VMware的NAT模式「建议收藏」什么是NAT模式?理论化的措辞我就不说了,我将结合本人平时的经验来简单的说明一下NAT模式,以及配置NAT模式时遇到的问题。大家都知道,我们的电脑要想联网,需要与交换机连接,假设交换机的网关为192.168.1.1,那么我们的电脑的ip必定在相同的网段,比如192.168.1.101,网关必定是192.168.1.1。现在我们向通过NAT模式将主机与虚拟机连接起来,即主机可以ping的通虚拟机…

    2022年6月16日
    32
  • java 哈希冲突

    java 哈希冲突问题一:什么是哈希冲突通过哈希函数产生的哈希值是有限的,而数据可能比较多,导致经过哈希函数处理后仍然有不同的数据对应相同的哈希值。这时候就产生了哈希冲突。问题二:怎么解决哈希冲突开放地址法;再哈希法;链地址法(拉链法);公共溢出区法。开放地址法:开放地址法处理冲突的基本原则就是出现冲突后按照一定算法查找一个空位置存放…

    2022年6月16日
    33

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号