使用客户端凭证保护API
分类: IdentityServer4教程 ◆ 标签: #Asp.Net core基础 #认证 #授权 #OpenId Connect #Identity Server #OAuth2 ◆ 发布于: 2023-05-27 22:58:10

本教程演示了使用客户端凭证保护API,你可以参考源码:
本节教程源码
准备工作
我们需要使用IdentityServer4提供的模板,使用如下的命令安装模板
dotnet new -i IdentityServer4.Templates
安装好模板之后,即可以在创建项目的时候应用模板
新建ASP.net Core应用,并设置IDS4
我们先创建好项目需要使用的目录结构
md quickstart
cd quickstart
md src
cd src
dotnet new is4empty -n IdentityServer
创建完成后请仔细检查新建的目录结构,需要注意的是该模板创建了一个用于配置IdentityServer4的配置文件config.cs
创建完成之后即可以使用自己喜欢的IDE或者编辑器进行代码编辑,如果你使用的是visual studio
, 使用如下的命令进行visual studio
创建解决方案
cd ..
dotnet new sln -n Quickstart
donet sln add .\src\IdentityServer\IdentityServer.csproj
注意
该模板创建的项目默认使用https协议,当项目运行在
Kestrel
上会在端口5001上监听,如果是运行在IISExpress
上则会在随机端口上监听,如果需要更改监听的端口编辑文件Properties\launchSettings.json
文件。需要注意的是在生产环境中一定要使用https
定义API Scope
需要注意的是在IDS4中有几个概念:
- API Scoe
- Client
一个API代表一个你应用中想要保护的资源,为了给与用户或者应用授权,需要先定义出来,这里的scope指的是多少个API,或者API的集合等等。使用模板创建的项目中默认是以Config.cs 中定义的类Config
定义需要保护的API, 如下图所示:
public static class Config { public static IEnumerable<ApiScoe> ApiScopes => new List<ApiScope> { new ApiScope("api1", "My Api") }; }
也可以从这里查看完整代码.
注意
考虑在产线环境中给API定义一个逻辑名字,这样其他的开发者可以使用逻辑名字通过标识服务器链接你的API, 如果API需要更换实际名字或者实现,由于客户端使用逻辑名字引用,从而达到不会影响的目的。
定义客户端
下一步是在IdentityServer4中定义客户端应用(需要通过IdentityServer存取API的应用), 需要注意的是在本节,我们的客户端应用场景是没有可以交互的普通用户,只需要客户端和API直接交互。
如下图所示添加客户端的定义:
public static IEnumberable<Client> Clients => new List<Client> { new Client { ChientId = "Client", //没有交互式的用户,使用客户端/机密 来进行认证 AllowedGrantTypes = GrantTypes.ClientCredentials, //用于加密的密码 ClientSecrets = { new Secret("secret".sha246()) }, //定义客户端可以访问的API Scope AllowedScopes = { "api1" } } };
我们可以将客户端的ClientId和ClientSecret类比为登录的用户名和密码。
配置IdentityServer
要配置IdentityServer需要在Startup.cs
的ConfigureService
方法,如下:
{ 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-know/openid-configuration, 就可以看到IdentityServer4的自动发现文档了,需要注意的是自动发现文档是一个标准的终结点,客户端或者其他需要访问API的应用根据该终结点取得必要的配置内容,如下图:
当第一次启动时,IdentityServer为自动创建一个sign Key, 保存在文件tempkey.jwk。
添加一个API
接下来我们向解决方案中添加一个API
我们先回到quickstart项目的根目录下,使用如下的命令创建一个新的API项目:
dotnet new webapi -n .\src\Api
然后将该项目添加到解决方案中
dotnet sln add .\src\Api\Api.csproj
同时配置该API项目运行在端口6001https://localhost:6001, 你可以通过修改文件Properties\launchSettings.json, 如下图:
"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添加认证,需要添加包引用
dotnet add .\\src\\api\\Api.csproj package Microsoft.AspNetCore.Authentication.JwtBearer
配置
最后一步是给API添加配置,配置起到的作用如下
- 验证传入的Token是来自可信任的颁发者
- 验证传入的Token是合法的。
更新配置
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(); }); } }
创建客户端
最后一步是创建一个客户端,该客户端要求一个Access Token, 然后使用该token 请求API, 我们在项目中创建一个控制台应用
dotnet new console -n Client
将控制台程序添加到解决方案中
cd ..
dotnet sln add .\src\Client\Client.csproj
添加包引用到该Client应用中
cd src
cd Client
dotnet add package IdentityModel
IdentityModel
模块包含一个客户端库用于使用自动发现的终端,通过这种方式你只需要知道IdentityServer的基础地址,该客户端既可以通过自动发现终端读取相应的元数据:
// discover endpoints from metadata var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001"); if (disco.IsError) { Console.WriteLine(disco.Error); return; }
通过自动发现终端取得元数据,从IdentityServer取回Token,用于访问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);
调用API
通常我们会使用Http认证头将Access Token送往API, 可以通过方法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)); }
运行图如下:
对API进行授权
添加API的授权以及授权范围
services.AddAuthorization(options => { options.AddPolicy("ApiScope", policy => { policy.RequireAuthenticatedUser(); policy.RequireClaim("scope", "api1"); }); });
也可以选择在路由系统中设置授权范围
app.UseEndpoints(endpoints => { endpoints.MapControllers() .RequireAuthorization("ApiScope"); });