系列导航及源代码

需求

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

目标

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

原理与思路

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

实现

定义配置类型

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

  1. "JwtSettings": {
  2. "validIssuer": "TodoListApi",
  3. "validAudience": "https://localhost:5050",
  4. "expires": 5
  5. }

Application/Configurations中添加JwtConfiguration类如下:

  • JwtConfiguration.cs
  1. namespace TodoList.Application.Common.Configurations;
  2. public class JwtConfiguration
  3. {
  4. public string Section { get; set; } = "JwtSettings";
  5. public string? ValidIssuer { get; set; }
  6. public string? ValidAudience { get; set; }
  7. public string? Expires { get; set; }
  8. }

方法1: 通过Configuration绑定实现

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

  • DependencyInjection.cs
  1. // 省略其他...
  2. // 添加认证方法为JWT Token认证
  3. var jwtConfiguration = new JwtConfiguration();
  4. configuration.Bind(jwtConfiguration.Section, jwtConfiguration);
  5. services
  6. .AddAuthentication(opt =>
  7. {
  8. opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
  9. opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
  10. })
  11. .AddJwtBearer(options =>
  12. {
  13. options.TokenValidationParameters = new TokenValidationParameters
  14. {
  15. ValidateIssuer = true,
  16. ValidateAudience = true,
  17. ValidateLifetime = true,
  18. ValidateIssuerSigningKey = true,
  19. // 改为使用配置类成员获取
  20. ValidIssuer = jwtConfiguration.ValidIssuer,
  21. ValidAudience = jwtConfiguration.ValidAudience,
  22. IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("SECRET") ?? "TodoListApiSecretKey"))
  23. };
  24. });

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

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

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

  1. // 省略其他...
  2. ValidIssuer = _jwtConfiguration.ValidIssuer,
  3. ValidAudience = _jwtConfiguration.ValidAudience

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

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

方法2: 通过IOptions配置实现

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

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

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

  • IdentityService.cs
  1. public IdentityService(
  2. ILogger<IdentityService> logger,
  3. IConfiguration configuration,
  4. UserManager<ApplicationUser> userManager,
  5. IOptions<JwtConfiguration> jwtOptions)
  6. {
  7. _logger = logger;
  8. _userManager = userManager;
  9. // 初始化配置对象
  10. _jwtConfiguration = jwtOptions.Value;
  11. }

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



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

一点扩展

扩展1: 关于配置热加载

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

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

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

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

  • IdentityService.cs
  1. public IdentityService(
  2. ILogger<IdentityService> logger,
  3. UserManager<ApplicationUser> userManager,
  4. IOptionsMonitor<JwtConfiguration> jwtOptions)
  5. {
  6. _logger = logger;
  7. _userManager = userManager;
  8. // 使用IOptionsMonitor加载配置
  9. _jwtConfiguration = jwtOptions.CurrentValue;
  10. }

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

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

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

  1. "JwtSettings": {
  2. "validIssuer": "TodoListApi",
  3. "validAudience": "https://localhost:5050",
  4. "expires": 10
  5. }

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

并把token解析:

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

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

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

  1. "JwtSettings": {
  2. "validIssuer": "TodoListApi",
  3. "validAudience": "https://localhost:5050",
  4. "expires": 5
  5. },
  6. "JwtApiV2Settings": {
  7. "validIssuer": "TodoListApiV2",
  8. "validAudience": "https://localhost:5050",
  9. "expires": 10
  10. }

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

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

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

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

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

总结

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

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

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

参考资料

  1. Options validation

使用.NET 6开发TodoList应用(26)——实现Configuration和Option的强类型绑定的更多相关文章

  1. 使用.NET 6开发TodoList应用(3)——引入第三方日志库

    需求 在我们项目开发的过程中,使用.NET 6自带的日志系统有时是不能满足实际需求的,比如有的时候我们需要将日志输出到第三方平台上,最典型的应用就是在各种云平台上,为了集中管理日志和查询日志,通常会选 ...

  2. 使用.NET 6开发TodoList应用(1)——系列背景

    前言 想到要写这样一个系列博客,初衷有两个:一是希望通过一个实践项目,将.NET 6 WebAPI开发的基础知识串联起来,帮助那些想要入门.NET 6服务端开发的朋友们快速上手,对使用.NET 6开发 ...

  3. 使用.NET 6开发TodoList应用(2)——项目结构搭建

    为了不影响阅读的体验,我把系列导航放到文章最后了,有需要的小伙伴可以直接通过导航跳转到对应的文章 : P TodoList需求简介 首先明确一下我们即将开发的这个TodoList应用都需要完成什么功能 ...

  4. 使用.NET 6开发TodoList应用(4)——引入数据存储

    需求 作为后端CRUD程序员(bushi,数据存储是开发后端服务一个非常重要的组件.对我们的TodoList项目来说,自然也需要配置数据存储.目前的需求很简单: 需要能持久化TodoList对象并对其 ...

  5. 使用.NET 6开发TodoList应用(5)——领域实体创建

    需求 上一篇文章中我们完成了数据存储服务的接入,从这一篇开始将正式进入业务逻辑部分的开发. 首先要定义和解决的问题是,根据TodoList项目的需求,我们应该设计怎样的数据实体,如何去进行操作? 长文 ...

  6. 使用.NET 6开发TodoList应用(5.1)——实现Repository模式

    需求 经常写CRUD程序的小伙伴们可能都经历过定义很多Repository接口,分别做对应的实现,依赖注入并使用的场景.有的时候会发现,很多分散的XXXXRepository的逻辑都是基本一致的,于是 ...

  7. 使用.NET 6开发TodoList应用(6)——使用MediatR实现POST请求

    需求 需求很简单:如何创建新的TodoList和TodoItem并持久化. 初学者按照教程去实现的话,应该分成以下几步:创建Controller并实现POST方法:实用传入的请求参数new一个数据库实 ...

  8. 使用.NET 6开发TodoList应用文章索引

    系列导航 使用.NET 6开发TodoList应用(1)--系列背景 使用.NET 6开发TodoList应用(2)--项目结构搭建 使用.NET 6开发TodoList应用(3)--引入第三方日志 ...

  9. 使用.NET 6开发TodoList应用(7)——使用AutoMapper实现GET请求

    系列导航 使用.NET 6开发TodoList应用文章索引 需求 需求很简单:实现GET请求获取业务数据.在这个阶段我们经常使用的类库是AutoMapper. 目标 合理组织并使用AutoMapper ...

随机推荐

  1. pwnable_start & ciscn_2019_es_2 & ez_pz_hackover_2016 & pwn2_sctf_2016

    花了两天时间做了这四道题,感觉收获很多.但是这种收获感觉写文章写不出自己的思路,就录制了一个视频. pwnable_start 这道题考察了系统调用,shellcode的编写,和动态调试的知识. ci ...

  2. LET函数(Excel函数集团)

    LET函数,是个Office365新增函数,所以,还在用上古版本的童鞋请无视此篇哈~ 话说Excel中,有个自定义名称的功能,如下图,左右两个表分别自定义了"data1"和&quo ...

  3. Kafka安装Kafka-Eagle可视化界面

    要先安装jdk 可以参考:https://www.cnblogs.com/pxblog/p/10512886.html 下载 http://download.kafka-eagle.org/ 上传到服 ...

  4. JAVA获取指定日期的一天的开始时刻(时间)和结束时刻(时间)

    注: SimpleDateFormat是线程不安全的 public static SimpleDateFormat format = new SimpleDateFormat("yyyyMM ...

  5. 从源码角度解析 Springboot 2.6.2 的启动过程

    1. 概述 老话说的好:把简单的事情重复做,做到极致,你就成功了. 言归正传,Springboot的启动过程,一直都是面试的高频点,今天我们用当前最新的 Springboot 2.6.2 来聊一聊 S ...

  6. 【LeetCode】769. Max Chunks To Make Sorted 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  7. 1036 - A Refining Company

    1036 - A Refining Company   PDF (English) Statistics Forum Time Limit: 3 second(s) Memory Limit: 32 ...

  8. javascript原始值和引用值类型及区别

    原始值和引用值类型及区别 首先,原始值和引用值类型都是js中的数据类型,为了充分利用存储空间,定义了不同的数据类型,而且js是弱类型,动态语言,数据类型可变. 原始值(简单数据类型) 存储在栈中的简单 ...

  9. Go语言练习 Rot13

    Go语言练习 Rot13 地址:https://tour.go-zh.org/methods/23 package main import ( "io" "os" ...

  10. 应用TYPE-C外围电源管理IC IM2605

    应用于TYPE-C外围集成同步4开关Buck-Boost变换器的电源管理IC   IM2605 IM2605描述 IM2605集成了一个同步4开关Buck-Boost变换器,在输入电压小于或大于输出电 ...