转自:https://www.cnblogs.com/hyqq/p/14138024.html;侵删。
Client Credentials
客户端应用不代表用户,客户端应用本身就相当于资源所有者
通常用于机器对机器的通信
客户端也需要身份认证
详细查看https://oauth.net/2/grant-types/client-credentials/
安装identityserver4模板
dotnet new -i identityserver4.templates //下载identityserver4模板 dotnet new is4inmem --name Idp //创建一个is4inmem模板的项目 名字为Idp
Config代码
using IdentityServer4.Models; using System.Collections.Generic; namespace Idp { public static class Config { public static IEnumerable<IdentityResource> IdentityResources => new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; public static IEnumerable<ApiScope> ApiScopes => new ApiScope[] { new ApiScope("scope1"), }; public static IEnumerable<ApiResource> ApiResources => new ApiResource[] { new ApiResource("api1","#api1") { //!!!重要 Scopes = { "scope1"} } }; public static IEnumerable<Client> Clients => new Client[] { // m2m client credentials flow client new Client { ClientId = "console client", ClientName = "Client Credentials Client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) }, AllowedScopes = { "scope1" } }, }; } }
startup代码
using IdentityServerHost.Quickstart.UI; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace Idp { public class Startup { public IWebHostEnvironment Environment { get; } public IConfiguration Configuration { get; } public Startup(IWebHostEnvironment environment, IConfiguration configuration) { Environment = environment; Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); //注册IdentityServer var builder = services.AddIdentityServer(options => { options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseSuccessEvents = true; // see https://identityserver4.readthedocs.io/en/latest/topics/resources.html options.EmitStaticAudienceClaim = true; }) //用户 .AddTestUsers(TestUsers.Users); // in-memory, code config builder.AddInMemoryIdentityResources(Config.IdentityResources); builder.AddInMemoryApiScopes(Config.ApiScopes); //添加API资源 builder.AddInMemoryApiResources(Config.ApiResources); builder.AddInMemoryClients(Config.Clients); // not recommended for production - you need to store your key material somewhere secure builder.AddDeveloperSigningCredential(); } public void Configure(IApplicationBuilder app) { if (Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseIdentityServer(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } } }
创建一个控制台应用 consoleclient
安装identitymodel库
Program代码
using IdentityModel.Client; using System; using System.Net.Http; using System.Threading.Tasks; namespace consoleclient { class Program { static async Task Main(string[] args) { // var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5001"); if (disco.IsError) { Console.WriteLine(disco.Error); return; } //请求accesstoken var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = disco.TokenEndpoint, ClientId = "console client", ClientSecret = "511536EF-F270-4058-80CA-1C89C192F69A", Scope = "scope1" }); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } Console.ReadKey(); } } }
启动2个项目 监听tokenresponse
创建空的web项目 添加Controller文件夹 添加IdentityController控制器
添加Microsoft.AspNetCore.Authentication.JwtBearer库
IdentityController代码
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Linq; namespace scope1 { [Route("api/[controller]")] [ApiController] [Authorize] public class IdentityController : ControllerBase { [HttpGet] public IActionResult Get() { return new JsonResult(from c in User.Claims select new { c.Type,c.Value} ); } } }
Startup代码
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace scope1 { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { //IdentityServer地址 options.Authority = "http://localhost:5000"; //对应Idp中ApiResource的Name options.Audience = "api1"; //不使用https options.RequireHttpsMetadata = false; }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); //身份验证 app.UseAuthentication(); //授权 app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } }
继续修改consoleclient项目的Program.cs
using IdentityModel.Client; using Newtonsoft.Json.Linq; using System; using System.Net.Http; using System.Threading.Tasks; namespace consoleclient { class Program { static async Task Main(string[] args) { var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000"); if (!disco.IsError) { //请求accesstoken var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = disco.TokenEndpoint, ClientId = "console client", ClientSecret = "511536EF-F270-4058-80CA-1C89C192F69A", Scope = "scope1" }); if (!tokenResponse.IsError) { //使用请求accesstoken访问被保护的资源 var apiClient = new HttpClient(); apiClient.SetBearerToken(tokenResponse.AccessToken); var response = await apiClient.GetAsync("http://localhost:5001/api/Identity"); var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); } } } } }
启动程序
打印结果