本文实现控制台应用程序连接IdentityServer服务端进行客户端验证,并调用授权API
1、新建控制台应用程序,nuget包安装 IdentityModel ,Program.cs代码如下:
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; } //ClientId = "consoleclient", // ClientName = "Client Credentials Client", // AllowedGrantTypes = GrantTypes.ClientCredentials, // ClientSecrets = { new Secret("consolesecret".Sha256()) }, // AllowedScopes = { "scope1" } var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = disco.TokenEndpoint, ClientId = "consoleclient", ClientSecret = "consolesecret", Scope = "scope2" }); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } Console.WriteLine(tokenResponse.Json); var apiClient = new HttpClient(); apiClient.SetBearerToken(tokenResponse.AccessToken); var response = await apiClient.GetAsync("http://localhost:6001/identity"); if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); } Console.ReadKey(); } }
2、IdentityServer服务沿用前面文章所建的项目,并修改config文件。
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"), new ApiScope("scope2"), }; public static IEnumerable<Client> Clients => new Client[] { // m2m client credentials flow client new Client { ClientId = "consoleclient", ClientName = "Client Credentials Client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("consolesecret".Sha256()) }, AllowedScopes = { "scope1","scope2" } }, new Client { ClientId = "wpfclient", ClientName = "wpfclient Credentials", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("wpfsecret".Sha256()) }, AllowedScopes = { "scope1","scope2" } }, // interactive client using code flow + pkce new Client { ClientId = "interactive", ClientSecrets = { new Secret("49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RedirectUris = { "https://localhost:44300/signin-oidc" }, FrontChannelLogoutUri = "https://localhost:44300/signout-oidc", PostLogoutRedirectUris = { "https://localhost:44300/signout-callback-oidc" }, AllowOfflineAccess = true, AllowedScopes = { "openid", "profile", "scope2" } }, new Client { ClientId = "mvc", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, // 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 } } }; }
3、新建Asp.net core API项目,nuget安装Microsoft.AspNetCore.Authentication.JwtBearer 包,修改Startup.cs文件。
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = "http://localhost:5001"; options.RequireHttpsMetadata = false; options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false }; }); services.AddAuthorization(options => { options.AddPolicy("ApiScope", policy => { policy.RequireAuthenticatedUser(); policy.RequireClaim("scope","scope2"); }); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); //.RequireAuthorization("ApiScope"); }); } }
4、Asp.net core API项目添加IdentityController.cs
[Route("identity")] [Authorize("ApiScope")] public class IdentityController : ControllerBase { [HttpGet] public IActionResult Get() { return new JsonResult( from c in User.Claims select new { c.Type, c.Value } ); } }
5、Asp.net core API项目添加NoScopeIdentityController.cs
[Route("noscopeIdentity")] [ApiController] [Authorize] public class NoScopeIdentityController : ControllerBase { [HttpGet] public IActionResult Get() { return new JsonResult( from c in User.Claims select new { c.Type, c.Value } ); } }
6.launchsettings.json
{ "profiles": { "IdsWebApi": { "commandName": "Project", "dotnetRunMessages": "true", "launchBrowser": true, "launchUrl": "weatherforecast", "applicationUrl": "http://localhost:6001", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }
7、添加wpf项目,Nuget安装 IdentitiModel 包
<Window x:Class="WpfClient.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfClient" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="40"></RowDefinition> <RowDefinition Height="40"></RowDefinition> <RowDefinition Height="40"></RowDefinition> <RowDefinition Height="40"></RowDefinition> <RowDefinition Height="40"></RowDefinition> <RowDefinition Height="40"></RowDefinition> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <Label Content="账号"/> <TextBox Name="txtaccount" Width="100" Height="30"/> </StackPanel> <StackPanel Orientation="Horizontal" Grid.Row="1"> <Label Content="账号"/> <TextBox Name="txtpassword" Width="100" Height="30"/> </StackPanel> <Button Grid.Row="2" Height="30" Width="100" Content="登录" Click="Button_Click"/> </Grid> </Window>
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { Login(); } public async Task Login() { var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5001"); if (disco.IsError) { Console.WriteLine(disco.Error); return; } var token = await client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = disco.TokenEndpoint, UserName = txtaccount.Text, Password = txtpassword.Text, ClientId = "wpfclient", ClientSecret = "wpfsecret", Scope = "scope1" }); if (token.IsError) { return; } Console.WriteLine(token.Json); var apiClient = new HttpClient(); apiClient.SetBearerToken(token.AccessToken); var response = await apiClient.GetAsync("http://localhost:6001/noscopeIdentity"); if (!response.IsSuccessStatusCode) { MessageBox.Show(response.StatusCode.ToString()); } else { var content = await response.Content.ReadAsStringAsync(); MessageBox.Show(content); } } }