IdentityServer4_1.ClientCredentials
Identityserver4
Client Credentials
客户端凭证模式
1.准备
dotnet CLI快速安装模板Identityserver模板
dotnet new -i IdentityServer4.Templates
2.创建Asp .net core应用程序
创建IdentityServery 项目
md quickstart
cd quickstart
md src
cd src
dotnet new is4empty -n IdentityServer
这将创建以下文件:
IdentityServer.csproj
- 项目文件和一个Properties\launchSettings.json
文件Program.cs
和Startup.cs
- 主应用程序入口点Config.cs
- IdentityServer 资源和客户端配置文件
创建Quickstart一个解决方案,更好的获得visual studio的支持
PS D:\quickstart\src> cd ..
PS D:\quickstart> dotnet new sln -n Quickstart
The template "Solution File" was created successfully.
把 IdentityServer 项目添加到解决方案中
dotnet sln add .\src\IdentityServer\IdentityServer.csproj
笔记
此模板中使用的协议是
https
,在 Kestrel 上运行时端口设置为 5001 或在 IISExpress 上运行时设置为随机端口。您可以在Properties\launchSettings.json
文件中更改它。对于生产场景,您应该始终使用https
.
Defining an API Scope
API 是系统中要保护的资源。资源定义可以通过多种方式加载,您用于创建上述项目的模板显示了如何使用“代码即配置”方法。
已经为您创建了 Config.cs。打开它,将代码更新为如下所示:
public static class Config
{
///定义API范围
public static IEnumerable<ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope("api1", "My API")
};
}
Defining the client
///定义客户端
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "client",///应用程序登录名
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())///应用程序密码
},
// scopes that client has access to
AllowedScopes = { "api1" }
}
};
配置身份服务器
加载资源和客户端定义发生在[Startup.cs 中]
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer()
.AddDeveloperSigningCredential() //This is for dev only scenarios when you don’t have a certificate to use.
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients);
// omitted for brevity
}
测试
浏览器测试
如果您运行服务器并将浏览器导航到https://localhost:5001/.well-known/openid-configuration
,您应该会看到所谓的发现文档。发现文档是身份服务器中的标准端点。您的客户端和 API 将使用发现文档来下载必要的配置数据。
通过Postman调用
3.创建WebAPI
向解决方案中添加API
src文件夹下运行一下命令
dotnet new webapi -n Api
将其添加到解决方案中
cd ..
dotnet sln add .\src\Api\Api.csproj
将 API 应用程序配置为https://localhost:6001
仅在其上运行。您可以通过编辑Properties 文件夹中的launchSettings.json文件来执行此操作。将应用程序 URL 设置更改为:
"applicationUrl": "https://localhost:6001"
控制器
添加一个名为 的新类IdentityController
:
[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 });
}
}
此控制器稍后将用于测试授权要求,以及通过 API 的眼睛可视化声明身份。
添加 Nuget 依赖项
为了使配置步骤工作,必须添加 nuget 包依赖项,请在根目录中运行以下命令:
dotnet add .\\src\\api\\Api.csproj package Microsoft.AspNetCore.Authentication.JwtBearer
因为包与项目框架不兼容,执行会报错。需要选定Microsoft.AspNetCore.Authentication.JwtBearer的对应版本
配置
最后一步是将身份验证服务添加到 DI(依赖注入)并将身份验证中间件添加到管道中。这些将:
- 验证传入的令牌以确保它来自受信任的发行者
- 验证令牌是否有效与此 API 一起使用(又名受众)
将启动更新为如下所示:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:5001";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
AddAuthentication
将身份验证服务添加到 DI 并配置Bearer
为默认方案。UseAuthentication
将身份验证中间件添加到管道中,因此每次调用主机时都会自动执行身份验证。UseAuthorization
添加授权中间件以确保匿名客户端无法访问我们的 API 端点。
https://localhost:6001/identity
在浏览器上导航到控制器应返回 401 状态代码。这意味着您的 API 需要凭证并且现在受 IdentityServer 保护。
4.Creating the client
src
目录下创建client
dotnet new console -n Client
添加到您的解决方案
cd ..
dotnet sln add .\src\Client\Client.csproj
Add the IdentityModel
NuGet package to your client.
cd src
cd client
dotnet add package IdentityModel
IdentityModel 包括一个与发现端点一起使用的客户端库
在main方法中添加如下内容
// discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
如果您在连接时遇到错误,则可能是您正在运行https并且其开发证书
localhost
不受信任。您可以运行以信任开发证书。这只需要做一次。dotnet dev-certs https --trust
接下来,您可以使用发现文档中的信息向 IdentityServer 请求令牌以访问api1
:
// request token
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "api1"
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
将访问令牌从控制台复制并粘贴到jwt.ms以检查原始令牌。
控制台输出[jwt.ms]
QsImV4cCI6MTYzNjQ0MjkyNCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NTAwMSIsImNsaWVudF9pZCI6ImNsaWVudCIsImp0aSI6IkY5ODEyOTZDODczMTkyOEJERkUyMTlEMzlFODRDMEQxIiwiaWF0IjoxNjM2NDM5MzI0LCJzY29wZSI6WyJhcGkxIl19.AF00jjPL1lrdftbJhfQNKmULL9-0lUyHh3os1KRDusp7s4PA_fvJ5VnIoPohQM266NCXHxKTMs5jXIQWRcyk95zv5iKhufBStqyH3IuG5NHF658bmQDBoVI06_KMNnQERoNmzlNcMERU0-ekyxQefPjKWLoUs7Vn3daJS22PYqz5L9PY5SxM69rQtijEJIZO9MeGxChu6suVZe0JDtn4960J9IbFwG1eOpbqxwdIpKVXDTPV8II29BL8qGk_Us_1AMThJIoUMj96V9CnN7u6uK_SUsQalXz9xEPdsTy3lbix5-uajpGIXYxNAfmSu7wysm487yztruNtshhL133JkQ","expires_in":3600,"token_type":"Bearer","scope":"api1"}
调用 API
要将访问令牌发送到 API,您通常使用 HTTP 授权标头。这是使用SetBearerToken
扩展方法完成的:
// call api
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync("https://localhost:6001/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
运行以后,输出:
默认情况下,访问令牌将包含有关范围、生命周期(nbf 和 exp)、客户端 ID (client_id) 和颁发者名称 (iss) 的声明。
API 授权
现在,API 接受您的身份服务器颁发的任何访问令牌。
在下文中,我们将添加允许检查范围是否存在于客户端请求(并被授予)的访问令牌中的代码。为此,我们将使用 ASP.NET Core 授权策略系统。将以下内容添加到Startup
的ConfigureServices
方法中:
services.AddAuthorization(options =>
{
options.AddPolicy("ApiScope", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("scope", "api1");
});
});
您现在可以在各个级别执行此策略,例如
- 全球
- 对于所有 API 端点
- 对于特定的控制器/动作
通常,您为路由系统中的所有 API 端点设置策略:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers()
.RequireAuthorization("ApiScope");
});
dotnet run --project D:\project\IdentityServerSampleNew\quickstart\1_ClientCredentials\src\IdentityServer\IdentityServer.csproj #Now listening on: https://localhost:5001
dotnet run --project D:\project\IdentityServerSampleNew\quickstart\1_ClientCredentials\src\Api\api.csproj #Now listening on: https://localhost:65002
dotnet run --project D:\project\IdentityServerSampleNew\quickstart\1_ClientCredentials\src\Client\Client.csproj
进一步的实验
本演练重点介绍了迄今为止的成功路径
- 客户端能够请求令牌
- 客户端可以使用令牌访问 API
您现在可以尝试引发错误以了解系统的行为方式,例如
-
尝试在 IdentityServer 未运行时连接到它(不可用)
-
尝试使用无效的客户端 ID 或机密来请求令牌
服务端信息
客户端返回信息
-
尝试在令牌请求期间请求无效范围
服务器:
客户端
-
尝试在未运行时调用 API(不可用)
- 不要将令牌发送到 API
- 将 API 配置为需要与令牌中的范围不同的范围
- 将令牌中的范围设置为与API不同的范围