系列導航及源代碼
- 使用.NET 6開發TodoList應用文章索引
需求
在上一篇文章使用.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>,在替換的程序中之需要注意以下兩點即可:
IOptionsSnapshot本身是注冊為ScopedService,所以不能注入到SingletonService中使用;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
參考資料
- Options validation
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/412742.html
標籤:.NET技术
上一篇:test
