使用Client Credentials模式控制API的访问
本文基于官方文章翻译和修改
客户端凭证模式(Client Credentials)模式
客户端凭证模式模式是IdentityServer4中控制API访问的基本模式,我们通常通过定义一个API和访问客户端,客户端通过客户端ID请求Identity Server以获取访问的
token
本篇将通过创建一个用户认证授权中心来保护API
编写Identity server
Install-Package IdentityServer4 -Version 3.1.0
定义API Resource和客户端
public static class Config
{
// Defining an API Resource
public static IEnumerable<ApiResource> Apis =>
new List<ApiResource>{
new ApiResource("CodeSnippets.WebApi","CodeSnippets API")
};
// Defining 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={ "CodeSnippets.WebApi" }
}
};
}
配置IdentityServer
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer()
.AddInMemoryApiResources(Config.Apis)
.AddInMemoryClients(Config.Clients);
// builder.AddDeveloperSigningCredential();
}
配置完成后运行应用,访问:http://localhost:5000/.well-known/openid-configuration将看到所谓的发现文档(发现文档是身份服务器中的标准端点,我们的客户端和API将使用发现文档来下载必要的配置数据)。
首次启动时,IdentityServer将为我们创建一个开发人员签名密钥,该文件名为tempkey.rsa。我们无需将该文件签入源代码管理中,如果不存在该文件将被重新创建。
创建API
- 新建一个WebApi:
[Route("identity")]
[ApiController]
[Authorize]
public class IdentityController : ControllerBase
{
public IActionResult Get()
{
return new JsonResult(User.Claims.Select(c => new { c.Type, char.MinValue }));
}
}
配置launchSettings.json使其在端口5001运行
- 配置API
public void ConfigureServices(IServiceCollection services)
{
// 将身份验证服务添加到DI并配置Bearer为默认方案。
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", option =>
{
option.Authority = "http://localhost:5000";
option.RequireHttpsMetadata = false;
option.Audience = "CodeSnippets.WebApi";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseAuthentication(); // 将身份验证中间件添加到管道中,以便对主机的每次调用都将自动执行身份验证。
app.UseAuthorization(); // 添加了授权中间件,以确保匿名客户端无法访问我们的API端点。
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute().RequireAuthorization();
//endpoints.MapHealthChecks("")
});
}
此时运行API,访问http://localhost:5001/identity
返回401 Unauthorized
创建客户端
客户端请求访问令牌,然后使用该令牌访问API
。
IdentityServer上的令牌端点实现了OAuth 2.0协议,我们可以使用原始HTTP来访问它。但是,我们有一个名为IdentityModel的客户端库,该客户端库将协议交互封装在易于使用的API中:
Install-Package IdentityModel -Version 4.1.1
-
步骤一 获取被发现文档
IdentityModel
包括一个与发现端点一起使用的客户端库。这样,我们只需要知道IdentityServer的基地址-实际的端点地址就可以从元数据中读取。 -
步骤二
使用发现文档中的信息向IdentityServer请求令牌以访问API
-
步骤三
调用API
static async Task Main(string[] args)
{
// 1.discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
// 2.request token
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "CodeSnippets.WebApi"
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("*".PadLeft(50, '*'));
// 3.call api
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync("http://localhost:5001/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
//Console.WriteLine(Newtonsoft.Json.Linq.JArray.Parse(content));
}
}
以上代码执行结果: