当前位置:网站首页>【One by One系列】IdentityServer4(二)使用Client Credentials保护API资源
【One by One系列】IdentityServer4(二)使用Client Credentials保护API资源
2022-06-23 18:00:00 【DDGarfield】
书接上回,我们将会正式开始介绍IdentityServer4。
IdentityServer4是实现了OAuth2.0+OpenId Connect两种协议的优秀第三方库,属于.net生态中的优秀成员。可以很容易集成至ASP.NET Core,颁发token。
使用Id4基本步骤如下:
- **1.**在
Startup.Configure方法中调用
app.UseIdentityServer();
添加中间件,把Id4添加至http请求处理管道,这使得Id4可以为OpenID Connect和OAuth2协议描述的端点(如/connect/token)请求提供服务。
- **2.**在
Startup.ConfigureServices中注册IdentityServer4
services.AddIdentityServer(options=>
{
...
});
- **3.**配置Identity Server
- Identity资源表示提供给客户端进行用户识别的信息(声明)。声明可能包括用户名称、电子邮件地址等。
- API资源表示用户可通过访问令牌访问的受保护数据或功能。API 资源的一个示例是要求授权的 Web API(或 API集合)。
- 用于签名的凭据(credentials)
- 用户可能会请求访问的Identity资源和API资源
- 会请求获取token的客户端
- 用户信息的存储机制,如ASP.NET Core Identity或者其他机制
当你指明Id4使用的客户端和资源,可以将IEnumerable<T>传递给接受内存中的客户端或资源存储的方法,如果在更复杂的场景,可以通过依赖注入的方式提供客户端和资源提供程序类型。
IdentityServer4 使用自定义 IClientStore 类型提供的内存中资源和客户端的示例配置:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//...
services.AddSingleton<IClientStore, CustomClientStore>();
services.AddIdentityServer()
.AddSigningCredential("CN=sts")
.AddInMemoryApiResources(MyApiResourceProvider.GetAllResources())
.AddAspNetIdentity<ApplicationUser>();
//...
}
经过上面的概述,我们先易后难,将从最简单的客户端凭证开始实战搭建IdentityServer4项目并以此保护api资源,首先客户端凭证属于OAuth2.0的一种授权方式。
- 主要是向IdentityServer发送post请求
token?grant_type=client_credentials& client_id=CLIENT_ID& client_secret=CLIENT_SECRET,获取access-token,以此来访问api资源。 - 在IdentityServer4中,增加了
Scope参数,表明了客户端的访问权限
1.安装Id4模板
dotnet new -i IdentityServer4.Templates
AdminUI:测试,生产环境需要交钱,商业软件ASP.NET Core Identity:结合ASP.NET Core IndentityEmpty:空模板Entity Frame Store:使用ef数据持久化身份认证信息In-Memory Stores and Test Users:添加内存中的用户认证信息,和测试用户Quickstart UI (UI assets only):UI
2.创建ASP.NET Core应用,搭载Id4
2.1 创建项目
- 使用IdentityServer4的空模板创建应用
md quickstart
cd quickstart
md src
cd src
#空模板 项目
dotnet new is4empty -n IdentityServer
The template "IdentityServer4 Empty" was created successfully.
- 项目添加解决方案
cd ..
dotnet new sln -n QuickStart
dotnet sln add .\IdentityServer\IdentityServer.csproj
2.2 修改launchSettings.json
- 测试环境,使用http,删掉IIS相关的配置
{
"profiles": {
"SelfHost": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": ".well-known/openid-configuration",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:5001/"
}
}
}
2.3 定义一个api scope
上篇与前文都介绍过,scope代表资源所有者在被保护资源那里的一些权限,可以把被保护资源分为不同的scope,具体的粒度由开发自定义。
- 模板中ApiScope为空,在Config.cs增加
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
//新增
new ApiScope("api1", "My API")
};
- 第二个参数,是
displayname
2.4 定义一个客户端
要让我们的IdentityServer给客户端颁发token,就要让客户端在IdentityServer注册。
客户端,模板中的客户端与scope一样为空,在Config.cs增加客户端,代码如下:
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "client app",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret-123456".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" }
}
};
“我们之后会增加这个定义的客户端,这个客户端将会访问
AllowedScopes指定的api scope。 ”
注意:在此场景下,客户端跟用户是没有交互的,身份认证是通过IdentityServer的客户密钥。
官方描述:你可以把ClientId和ClientSecret看作应用程序本身的登录名和密码。它向身份服务器表明您的应用程序的身份(我是xx应用程序,想访问服务器)。
2.5 注册IdentityServer
注释模板代码Startup.ConfigureServices()所有代码,增加代码:加载定义的资源和客户端,代码如下:
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer()
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients);
// omitted for brevity
}
配置完成。运行并浏览器访问http://localhost:5001/.well-known/openid-configuration,就能看到discovery document.
- 它是IdentityServer中的标准端点
- 客户端和APIs会使用它下载必要的配置数据,容后再表
在第一次启动时,IdentityServer将创建一个开发者签名密钥,它是一个名为tempkey.rsa的文件。您不必将该文件签入源代码版本控制,如果不存在该文件,它将被重新创建。
3.创建webapi
限制开始创建我们需要保护的api资源
3.1 新建项目
dotnet new webapi -n webapi
cd ..
dotnet sln add .\webapi\webapi.csproj
3.2 修改launchSettings.json
{
"profiles": {
"Api": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://localhost:6001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
3.3 新增api
IdentityController.cs
[Route("api/[controller]")]
[ApiController]
public class IdentityController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
3.4 引入nuget包
Microsoft.AspNetCore.Authentication.JwtBearer
这个包是当收到请求时,对授权头中JWT的具体身份认证
dotnet add .\webapi\webapi.csproj package Microsoft.AspNetCore.Authentication.JwtBearer
3.5 注册服务和添加中间件
最后一步是将身份认证服务添加到依赖注入中,并将身份认证中间件添加到管道中。以便:
- 验证传入的token,确保token来自可信的颁布者(服务器)
- 验证这个token在这个api中使用是有效的(也就是受众)
看代码:
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
//token颁发者
options.Authority = "http://localhost:5001";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
options.RequireHttpsMetadata = false;
});
services.AddAuthorization(options =>
{
options.AddPolicy("ApiScope", policy =>
{
policy.RequireAuthenticatedUser();
//scope
policy.RequireClaim("scope", "api1");
});
});
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireAuthorization("ApiScope");
});
}
}
AddAuthentication:增加认证服务到依赖注入,注册Bearer作为默认schemeAddAuthorization:增加授权服务到依赖注入,验证token中是否存在scope,这里使用的是ASP.NET Core授权策略系统 “这里实质是验证jwt中的payload的scope ”RequireHttpsMetadata用于测试目的;将此参数设置为 false,可在你没有证书的环境中进行测试。在实际部署中,JWT 持有者令牌应始终只能通过 HTTPS 传递。UseAuthentication:添加认证中间件,以便对host的每次调用自动执行身份认证,此中间件准备就绪后,会自动从授权标头中提取 JWT 令牌。然后对其进行反序列化、验证,,并将其存储为用户信息,稍后供 MVC 操作或授权筛选器引用。 “JWT 持有者身份验证中间件还可以支持更高级的方案,例如颁发机构authority 不可用时使用本地证书验证令牌。对于此情景,可以在JwtBearerOptions对象中指定TokenValidationParameters对象。 ”UseAuthorization:添加授权中间件,以确保我们的api不会被匿名客户端访问RequireAuthorization("ApiScope"):全局执行授权策略 “除了全局以外,还可以针对多有的api端点,或者特定的controller,action,根据实际的业务场景灵活变化吧 ”
访问:http://localhost:6001/identity,返回状态码401,这是api要求凭证,所以现在api是被IdentityServer保护着
4.创建客户端
最后一步,创建一个由IdentityServer管理的客户端,并通过客户端请求access-token,然后访问api
4.1 新建项目
dotnet new console -n Client
dotnet sln add .\Client\Client.csproj
4.2 引入nuget包
需要引入IdentityModel包,一个客户端类,以请求disconvery endpoint
cd .\Client\
dotnet add package IdentityModel
4.3 编码-请求Idisconvery endpoint
只需要知道IdentityServer的基础地址,实际的各类端点地址就可以从元数据中读取:
class Program
{
static async Task Main(string[] args)
{
// discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5001");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
}
}
4.4 编码-请求access token
这一步,使用从discovery document中获取的信息,向IdentityServer请求一个访问api1的token:
// request token
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
//在IdentityServer注册的id与secret
ClientId = "client app",
ClientSecret = "secret-123456",
Scope = "api1"
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
注意看,这里的ClientId,ClientSecret必须与2.4中定义客户端保持一致,Scope也要与AllowedScopes保持一致。
4.5 编码-调用api
在这一步,使用扩展方法SetBearerToken,这个方法主要组装http请求:授权头+access token,并以此请求访问api资源:
// call api
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync("http://localhost:6001/api/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
5.测试
- 启动IdentityServer
cd .\IdentityServer\
dotnet run
- 启动webapi
cd .\webapi\
dotnet run
- 用vs启动client
获取access-token,我们通过http://jwt.calebb.net/解析
这也是api返回的Claims
“身份认证的中间对JWT进行了身份认证后,会把解析到的Claims组装进HttpContext,以供下一个中间件(如授权中间件)调用 ”
接下来我们就去触发不同的错误去了解IdentityServer是如何工作的,我选择其中几个比较有意义的测试:
5.1 使用一个无效客户端id或者密钥请求token
没被注册的客户端,访问时,所以是invalid_client
类比场景:去办理门禁卡,物业没找到你这个业主信息,办个鬼呀
5.2 在请求token时指定无效的scope
请求token,指定的scope,在indentityserver中并不存在,所以是invalid_scope
类比场景:去办理门禁卡,小区一共10栋,你去办11栋,办个鬼呀
5.3 请求api时,不传入toekn
不传入token,那么webapi就没收到token,所以返回Unauthorized未授权
类比场景:进入小区,没有门禁,肯定不让你进
5.4 修改API对scope的验证要求
被保护的资源webapi中配置plicy.RequireClaim("scope","api2");
而客户端指定的scope是api1
客户端是有access-token,具有进入系统凭证,但是,只允许scope为api2的访问,传入的时api1,当然就返回Forbidden
类比场景:小区进入后,进入单元楼,明明是3栋2单元的楼宇,但是你的门禁只能针对3栋1单元,当然也不会刷开2单元的大门
参考链接
https://identityserver4.readthedocs.io/en/latest/quickstarts/1_client_credentials.html#source-code
边栏推荐
- 高级计网笔记(九)
- Leetcode: hash table 04 (sum of two numbers)
- What does logistics service and management mainly learn
- 杰理之播 MP3 提示音功能【篇】
- 启示录《贝索斯的商业逻辑与领导力法则》
- 【NOI2014】15. Difficult to get up syndrome [binary]
- 元宇宙大杀器来了!小扎祭出4款VR头显,挑战视觉图灵测试
- Yapi installation
- PISCES: A Programmable, Protocol-Independent Software Switch(总结)
- Obtain equipment information
猜你喜欢

Nanxin semiconductor rushes to the scientific innovation board: its annual revenue is RMB 980 million. Sequoia Xiaomi oppo is the shareholder

Description of all shortcut keys in win11 system

This year, Anhui master fund exploded

杰理之DAC 输出方式设置【篇】

傑理之串口設置好以後打印亂碼,內部晶振沒有校准【篇】

从零开发小程序和公众号【第一期】

Learn the basic principles of BLDC in Simulink during a meal

halcon知识:区域(Region)上的轮廓算子(1)

Yaxiang spice listed on Shenzhen Stock Exchange: with a market value of 4billion, Dinglong Bohui and Yongyao investment are shareholders

Leetcode question brushing: hash table 01 (valid Letter ectopic words)
随机推荐
TimerTasks笔记
【翻译】具有时间结构的特定信号的鲁棒提取(下)
Shengke communication IPO meeting: annual revenue of 460million China Zhenhua and industry fund are shareholders
在Microsoft Exchange Server 2007中安装SSL证书的教程
杰理之添加定时器中断【篇】
NetCF总结
Leetcode question brushing: hash table 01 (valid Letter ectopic words)
QT based graphics rendering system documentation + project source code and executable exe files + system instructions
物联网平台搭建设备,附源代码
VirtP4笔记
韬略生物冲刺科创板:年亏损过亿 实控人张大为夫妇为美国籍
【Qt】第三、四章:窗口部件、布局管理
sed replace \tPrintf to \t//Printf
STM32 (VIII) -- PWM output
为什么要创建公开的OKR?
【Qt】选择题
【NOI2014】15.起床困难综合症【二进制】
Requirements and precautions for applying for multi domain SSL certificate
Une fois que le port série de Jerry est réglé, le Code aléatoire est imprimé, et le cristal interne n'est pas étalonné [chapitre]
杰理之串口通信 串口接收 IO 需要设置数字功能【篇】