首页 技术 正文
技术 2022年11月11日
0 收藏 370 点赞 4,373 浏览 4664 个字

系列导航及源代码

需求

在上一篇文章使用.NET 6开发TodoList应用(25)——实现RefreshToken中,我们通过使用Configuration获取方法GetSection拿到写在appsettings.Development.json中JWT的相关配置字段,这样实现没有问题,但是我们有更好的选择:通过使用强类型的Configuration绑定方法,或者通过Options相关方法来实现。在本文中我们将会分别来看一下这两种方法的实现。

目标

实现配置字段的强类型绑定,分别通过Configuration绑定和Options实现。

原理与思路

要实现强类型绑定,首先我们需要定义这个配置类型。然后根据需求,选择使用Configuration绑定实现或者使用Options配置实现。二者实现的功能上有一些区别:使用Options模式提供了更多的功能,如校验、热加载,也更方便进行测试。

实现

定义配置类型

根据我们在appsettings.Development.json中的配置:

"JwtSettings": {
"validIssuer": "TodoListApi",
"validAudience": "https://localhost:5050",
"expires": 5
}

Application/Configurations中添加JwtConfiguration类如下:

  • JwtConfiguration.cs
namespace TodoList.Application.Common.Configurations;public class JwtConfiguration
{
public string Section { get; set; } = "JwtSettings";
public string? ValidIssuer { get; set; }
public string? ValidAudience { get; set; }
public string? Expires { get; set; }
}

方法1: 通过Configuration绑定实现

修改Infrastructure项目中的DependencyInjection添加认证方法的逻辑:

  • DependencyInjection.cs
// 省略其他...
// 添加认证方法为JWT Token认证
var jwtConfiguration = new JwtConfiguration();
configuration.Bind(jwtConfiguration.Section, jwtConfiguration);services
.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
// 改为使用配置类成员获取
ValidIssuer = jwtConfiguration.ValidIssuer,
ValidAudience = jwtConfiguration.ValidAudience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("SECRET") ?? "TodoListApiSecretKey"))
};
});

修改IdentityService中的逻辑,添加一个私有字段

  • IdentityService.cs
// 添加JWT配置类字段
private readonly JwtConfiguration _jwtConfiguration;// 构造函数中进行初始化
// 初始化配置对象
_jwtConfiguration = new JwtConfiguration();
configuration.Bind(_jwtConfiguration.Section, _jwtConfiguration);

并将所有之前使用json对象获取字段值的地方都修改成通过私有字段的成员变量获取:

// 省略其他...
ValidIssuer = _jwtConfiguration.ValidIssuer,
ValidAudience = _jwtConfiguration.ValidAudience

验证如下,我们还是通过获取refresh token来检查配置是否成功:

看起来没什么问题。下面我们看第二种方法,也是相对比较推荐的做法。

方法2: 通过IOptions配置实现

我们在Infrastructure/DependencyInjection.cs中添加使用IOptions的配置:

  • DependencyInjection.cs
// 使用IOptions配置
services.Configure<JwtConfiguration>(configuration.GetSection("JwtSettings"));

然后通过依赖注入的方式去修改IdentityService

  • IdentityService.cs
public IdentityService(
ILogger<IdentityService> logger,
IConfiguration configuration,
UserManager<ApplicationUser> userManager,
IOptions<JwtConfiguration> jwtOptions)
{
_logger = logger;
_userManager = userManager;
// 初始化配置对象
_jwtConfiguration = jwtOptions.Value;
}

其他的不需要再进行修改。下面来验证一下效果,验证方法和刚才一致:

可以看到依然是没有问题的。

一点扩展

扩展1: 关于配置热加载

综合我们刚才提到的和所演示的可以看到,我们并没有演示关于配置热加载的功能,如果在程序的运行过程中,我们希望配置文件的改动能够直接反映到应用中,不需要重启应用,可以预想到这个功能还是很重要的。

这个功能是通过IOptionsSnapshot或者IOptionsMonitor来实现的,我们所需要做的就是在依赖注入的时候使用IOptionsSnapshot<JwtConfiguration>或者IOptionsMonitor<JwtConfiguration>代替我们之前使用的IOptions<JwtConfiguration>。在替换的过程中之需要注意以下两点即可:

  1. IOptionsSnapshot本身是注册为ScopedService,所以不能注入到SingletonService中使用;
  2. IOptionsMonitor本身注册为了SingletonService,所以可以注入到SingletonService中使用,但是在取值的时候不是使用Value而是使用CurrentValue

我们使用IOptionsMonitor来举例子验证,只需要修改IdentityService中构造函数的注入部分:

  • IdentityService.cs
public IdentityService(
ILogger<IdentityService> logger,
UserManager<ApplicationUser> userManager,
IOptionsMonitor<JwtConfiguration> jwtOptions)
{
_logger = logger;
_userManager = userManager;
// 使用IOptionsMonitor加载配置
_jwtConfiguration = jwtOptions.CurrentValue;
}

重新运行项目,我们先直接请求Token:

解析出的payload如下,过期时间是之前设置的5分钟后:

在不重启应用的情况下,我们去修改appsettings.Development.json中关于过期时间的配置,将过期时间设置为10分钟:

"JwtSettings": {
"validIssuer": "TodoListApi",
"validAudience": "https://localhost:5050",
"expires": 10
}

再次执行获取Token的请求,查看Header里的Date字段值:

并把token解析:

这个过期时间已经变成10分钟后了,大家可以自己动手试一下。

扩展2: 关于相同类型的多个配置Section处理

有一种情况是在appsettings.Development.json中我们可能会做这样的配置:

"JwtSettings": {
"validIssuer": "TodoListApi",
"validAudience": "https://localhost:5050",
"expires": 5
},
"JwtApiV2Settings": {
"validIssuer": "TodoListApiV2",
"validAudience": "https://localhost:5050",
"expires": 10
}

面对这种情况,我们可以在进行IOptions配置时指定配置名称,像这样:

// 使用IOptions配置
services.Configure<JwtConfiguration>("JwtSettings", configuration.GetSection("JwtSettings"));
services.Configure<JwtConfiguration>("JwtApiV2Settings", configuration.GetSection("JwtApiV2Settings"));

而在需要注入使用的地方也指定对应要进行配置的名称即可:

// 使用IOptionsMonitor加载配置
_jwtConfiguration = jwtOptions.Get("JwtApiV2Settings");

这样就可以正确地使用相应的配置了,就不再继续演示了。

总结

关于三种IOptions的对比见下表:

类型 依赖注入类型 是否支持配置热加载 配置加载更新时机 是否支持名称配置
IOptions<T> Singleton注入 只在程序运行开始时绑定一次,以后每次获取的都是相同值
IOptionsSnapshot<T> Scoped注入 每次请求时都会重新加载配置
IOptionsMonitor<T> Singleton注入 配置的值被缓存起来了,当原始配置发生变化时立即发生更新

在本文中我们介绍了如何使用强类型绑定配置项,以及如何实现配置的热加载。对于没有涉及到的诸如配置项的校验等内容(可以通过Annotation实现校验)可以参考官方文档:Options validation

参考资料

  1. Options validation
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,954
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,479
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,291
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,108
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,740
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,774