IdentityServer4 实践- 使用客户端凭据保护API
1-创建公共类库 IDS4.Core
1.1-引用 IdentityServer4
1.2-创建 Config.cs 配置类
1 using IdentityServer4.Models; 2 3 namespace IDS4.Core.ClientCredentials 4 { 5 /// <summary> 6 /// 配置文件(使用客户端凭据) 7 /// </summary> 8 public static class Config 9 { 10 private const string api_scope_one = "api1"; 11 12 public static IEnumerable<ApiScope> ApiScopes => 13 new List<ApiScope> 14 { 15 new ApiScope(api_scope_one, "第一个API") 16 }; 17 18 public static IEnumerable<Client> Clients => 19 new List<Client> 20 { 21 new Client 22 { 23 //可以将 ClientId 和 ClientSecret 视为应用程序本身的登录名和密码 24 ClientId = "client", 25 26 // 没有交互式用户,使用 clientid/secret 进行身份验证 27 AllowedGrantTypes = GrantTypes.ClientCredentials, 28 29 // 用于身份验证的密钥 30 ClientSecrets = 31 { 32 new Secret("secret".Sha256()) 33 }, 34 35 // 客户端有权访问的范围 36 AllowedScopes = { api_scope_one } 37 } 38 }; 39 40 } 41 }
2-创建身份服务器( Identity Server)IDS4.Server
PS:使用站点空模板
2.1-引用IDS4.Core
2.2-配置ConfigService
1 builder.Services.AddIdentityServer() 2 // 这仅适用于没有证书可以使用的开发场景。 3 .AddDeveloperSigningCredential() 4 .AddInMemoryApiScopes(IDS4.Core.ClientCredentials.Config.ApiScopes) 5 .AddInMemoryClients(IDS4.Core.ClientCredentials.Config.Clients);
2.3-配置ConfigApp
1 var app = builder.Build(); 2 app.UseIdentityServer();
Ps:完整的Program.cs
1 var builder = WebApplication.CreateBuilder(args); 2 3 builder.Services.AddIdentityServer() 4 // 这仅适用于没有证书可以使用的开发场景。 5 .AddDeveloperSigningCredential() 6 .AddInMemoryApiScopes(IDS4.Core.ClientCredentials.Config.ApiScopes) 7 .AddInMemoryClients(IDS4.Core.ClientCredentials.Config.Clients); 8 9 10 var app = builder.Build(); 11 app.UseIdentityServer(); 12 13 app.MapGet("/", () => "Hello World!"); 14 15 app.Run();
2.4-端口 5010
3-创建API站点 IDS4.API
PS:选 ASP.Net Core Web API 模板
3.1-引用 Microsoft.AspNetCore.Authentication.JwtBearer 类库
3.2-配置ConfigService
1 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 2 .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => 3 { 4 //验证传入的令牌以确保它来自受信任的发行者 5 options.Authority = "http://localhost:5010"; 6 // 禁用HTTPS 7 options.RequireHttpsMetadata = false; 8 options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters 9 { 10 //验证令牌是否有效与此 API 一起使用(又名 audience 受众) 11 ValidateAudience = false 12 }; 13 });
3.3-配置ConfigApp
app.UseAuthentication();
Ps:完整的Program.cs
1 using Microsoft.AspNetCore.Authentication.JwtBearer; 2 3 var builder = WebApplication.CreateBuilder(args); 4 5 // Add services to the container. 6 7 builder.Services.AddControllers(); 8 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 9 .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => 10 { 11 //验证传入的令牌以确保它来自受信任的发行者 12 options.Authority = "http://localhost:5010"; 13 // 禁用HTTPS 14 options.RequireHttpsMetadata = false; 15 options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters 16 { 17 //验证令牌是否有效与此 API 一起使用(又名 audience 受众) 18 ValidateAudience = false 19 }; 20 }); 21 // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 22 builder.Services.AddEndpointsApiExplorer(); 23 builder.Services.AddSwaggerGen(); 24 25 var app = builder.Build(); 26 27 // Configure the HTTP request pipeline. 28 if (app.Environment.IsDevelopment()) 29 { 30 app.UseSwagger(); 31 app.UseSwaggerUI(); 32 } 33 34 app.UseAuthentication(); 35 app.UseAuthorization(); 36 37 app.MapControllers(); 38 39 app.Run();
3.4-端口 5011
3.5-添加 IdentityController 控制器
1 using Microsoft.AspNetCore.Authorization; 2 using Microsoft.AspNetCore.Mvc; 3 4 // For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 5 6 namespace IDS4.API.Controllers 7 { 8 [Route("api/[controller]")] 9 [ApiController] 10 [Authorize] 11 public class IdentityController : ControllerBase 12 { 13 [HttpGet] 14 public IActionResult Get() 15 { 16 return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); 17 } 18 } 19 }
4-创建客户端 IDS4.Web
4.1-引用 IdentityModel 类库
4.2-从元数据中发现端点
1 var client = new HttpClient(); 2 var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest 3 { 4 Address = "http://localhost:5010", 5 Policy = new DiscoveryPolicy { RequireHttps = false } //禁用Https 6 }); 7 if(disco==null || disco.IsError) 8 { 9 Console.WriteLine(disco.Error); 10 return; 11 }
就是解析 http://localhost:5010/.well-known/openid-configuration 这个地址
4.3-请求令牌
1 // 请求令牌 2 var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest 3 { 4 Address = disco.TokenEndpoint, 5 6 ClientId = "client", 7 ClientSecret = "secret", 8 Scope = "api1" 9 }); 10 11 if (tokenResponse.IsError) 12 { 13 Console.WriteLine(tokenResponse.Error); 14 return; 15 } 16 17 Console.WriteLine(tokenResponse.Json);
4.4-调用API
1 // 调用api 2 var apiClient = new HttpClient(); 3 apiClient.SetBearerToken(tokenResponse.AccessToken); 4 5 var response = await apiClient.GetAsync("https://localhost:6001/identity"); 6 if (!response.IsSuccessStatusCode) 7 { 8 Console.WriteLine(response.StatusCode); 9 } 10 else 11 { 12 var content = await response.Content.ReadAsStringAsync(); 13 Console.WriteLine(JArray.Parse(content)); 14 }
Ps:完整的Index.cshtml.cs 的get 方法
1 public async Task OnGet() 2 { 3 var client = new HttpClient(); 4 var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest 5 { 6 Address = "http://localhost:5010", 7 Policy = new DiscoveryPolicy { RequireHttps = false } //禁用Https 8 }); 9 if (disco != null) 10 { 11 if (disco.IsError) 12 ViewData["data"] = disco.Error; 13 else 14 { 15 var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest 16 { 17 Address = disco.TokenEndpoint, 18 ClientId = "client", 19 ClientSecret = "secret", 20 Scope = "api1" 21 }); 22 if (tokenResponse != null) 23 { 24 if (tokenResponse.IsError) 25 ViewData["data"] = tokenResponse.Json; 26 else 27 { 28 var apiClient = new HttpClient(); 29 apiClient.SetBearerToken(tokenResponse.AccessToken); 30 31 var response = await apiClient.GetAsync("http://localhost:5011/api/identity"); 32 if (!response.IsSuccessStatusCode) 33 { 34 Console.WriteLine(response.StatusCode); 35 ViewData["data"] = response.StatusCode; 36 } 37 else 38 { 39 var content = await response.Content.ReadAsStringAsync(); 40 ViewData["data"] = content; 41 Console.WriteLine(JArray.Parse(content)); 42 } 43 } 44 } 45 else 46 { 47 ViewData["data"] = "访问失败"; 48 } 49 } 50 } 51 else 52 { 53 ViewData["data"] = "访问失败"; 54 } 55 }
4.5-Index.cshtml
PS:鉴权时序图
漫漫人生,唯有激流勇进,不畏艰险,奋力拼搏,方能中流击水,抵达光明的彼岸