本文实现控制台应用程序连接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);
}
}
}