【笔记】ASP.NET Core技术内幕与项目实现:基于DDD与前后端分离

news2025/1/15 6:52:48

最近在写论文,想使用ASP.NET Core Web API技术,但对它还不是很熟,鉴权组件也没用过,于是在网上查找资料,发现了杨中科老师写的这本书(微信读书上可以免费看),说起来我最初自学C#时看过他的盗版视频。

这本书使用的是.NET 6,但我用.NET Core 3.1也能实现书中的绝大部分功能,前几章主要讲理论,框架是简单的控制台,后面会使用到 ASP.NET Core Web API和鉴权组件。

但后来我发现要使用Identity鉴权组件需要使用EF Core的Code First模式,这种模式不符合我的习惯,我打算去掉这部分的功能,只实现到用JWT获取到Token为止,只判断能否登陆,不判断角色。

第一章 .NET Core入门

.NET Core的优点

1、.NET Core支持独立部署,也就是说,可以把.NET Core运行时环境和开发的程序打包到一起部署。这样就不需要在服务器上安装.NET Core运行环境,只要把程序复制到服务器上,程序就能运行,这对容器化、无服务器(Serverless)等非常友好。

2、ASP.NET Core程序内置了简单且高效的Web服务器—Kestrel。Kestrel被嵌入ASP.NET Core程序中运行,因此整个ASP.NET Core程序其实就是一个控制台程序。Kestrel可被配置上安全、HTTPS、限流、压缩、缓存等功能,从而成为直接面向终端用户的Web服务器,这样网站运行不依赖于IIS;也可以将其配置成轻量级的Web服务器,而安全、HTTPS、限流、压缩、缓存等功能则由部署在它前面的IIS、Nginx等反向代理服务器完成。

以上两个优点我觉得是很明显的,支持独立部署可以减少运维的工作量,不用再去安装运行环境,内置服务器是跨平台的基础。

.NET Core对.NET Framework中哪些技术不支持

我使用过的框架包括:Winform,WPF,ASP.NET WebForms,WCF

Windows特有的技术:绘图,Windows服务,Windows注册表,在.NET Core中,我们可以通过Microsoft.Windows.Compatibility Nuget包继续使用这些技术,但是使用这些技术开发的程序只能运行在Windows下。如需移植到其他平台,可通过重写这些代码或编写平台检测代码。

.NET Standard

.NET Standard是一套.NET API规范,不是具体的实现,它是为了开发人员从.NET Framework过渡到.NET Core时迁移代码的工作尽可能得减少,提高了代码的复用性。在日常的开发过程中,在使用Nuget时我们会经常看到它,那么如何选择呢?下图是.NET Starnard与.NET Core,.NET Framework版本之间的关系,可以看到.NET Standard的版本越高,对应.NET Core和.NET Framework的版本也越高,兼容性虽然有所下降,但可实现的功能却提高了很多,例如.NET Standard 1.0 提供 37,118 个可用 API 中的 7,949 个,.NET Standard 2.0 提供 37,118 个可用 API 中的 32,638 个,到了.NET Standard 2.1 提供 37,118 个可用 API 中的 37,118 个。官方推荐是.NET Standard 2.0,因为它实现了最大公约数。

 

第二章 .NET Core重难点知识

异步编程

什么是异步?它和同步有什么区别?

作者举了一个点餐的例子,同步是你在点餐时服务员一直站在那里等你决定吃什么,选好后提交上去;异步是在点餐时给你一张菜单然后就去服务其他人,等你选好后让服务员过来将菜单递交上去,这个过程可能比前一种方法慢一些,但能够同时服务更多的人。

结论就是异步比同步的优势在于可以增加同时处理的请求数,但响应的时间不一定比它快。

C#中使用async声明一个异步方法,await调用一个异步方法,确认一个方法是不是异步方法可以参考返回值是否是Task或者Task,按照约定方法名使用Async结尾,微软引入它的目的是降低异步编程的难度,具体的例子:

        private async void button2_Click(object sender, EventArgs e)
        {
            // 异步调用,调用了有async的异步方法,为了让它实现异步调用,需使用await关键字,并且方法自身
            // 添加async关键字修饰
            this.textBox1.Text = "查询开始\r\n" + this.textBox1.Text;
            var result = await QueryAsync();
            this.textBox1.Text = "查询结束\r\n" + this.textBox1.Text;
        }
        
        private async Task<List<string>> QueryAsync()
        {
            // 异步调用,Task.Delay返回Task,那么就可以使用await关键字调用,这样就是调用异步方法了
            // 调用异步方法的代价就是,需要将方法自身添加async关键字,并在方法的后缀添加Async标记
            await Task.Delay(5000);
            return null;
        }

Delay方法的声明

        public static Task Delay(int millisecondsDelay);

同步方法中使用异步方法

我们也可以使用同步的方式调用异步方法,只要不使用async和await关键字,用同步的方式包装含异步的方法时,需要返回空的Task时可使用Task.CompletedTask,返回自定义的Task时使用Task.FromResult(string msg)。

当不能使用async和await关键字时如何使用异步方法

调用返回值为Task类型时可以在异步方法后继续调用GetAwaiter().GetResult(),但这种方式不推荐。

异步休眠方法:Task.Delay(int millisecondsDelay),不要使用Thread.Sleep(int millisecondsDelay)会引起调用线程的阻塞。

同时执行多个异步方法

使用Task.WhenAll(),可以同时执行多个任务,并且等待所有任务执行完毕后再返回结果,适用于需要将一个任务拆分成多个子任务的场景,可以提高查询的速度。如果各个任务返回的Task值不一致,需要用到WhenAll(IEnumerable tasks),传递一个Task的列表进去。

使用Task.WhenAny(),也可以同时执行多个任务,但它是只要有一个任务执行完毕就会返回结果,获取任务的结果前需要对Status属性判断是否为TaskStatus.RanToCompletion,再去取结果,否则会引起线程阻塞。

LINQ

扩展方法

判断一个集合是否有一条数据时,不仅可以使用Count方法,也可以使用Any方法,而且这种方法的效率比前者更高。

获取一条数据时,我常常会使用FirstOrDefault方法,但这个方法有局限性,如果返回的结果应该只有一条,但实际返回了多条,该方法也会正常返回,隐藏异常信息。使用SingleOrDefault方法可以在返回多条时会抛出异常,开发人员也能及时的发现问题,提高程序的正确性。

第三章 .NET Core核心基础组件

依赖注入

负责提供对象的注册和获取功能的框架叫作“容器”,注册到容器中的对象叫作“服务”(service)

使用依赖注入时需要引用Microsoft.Extensions.DependencyInjection

依赖注入的三个生命周期

(1)瞬态(transient):每次被请求的时候都会创建一个新对象。这种生命周期适合有状态的对象,可以避免多段代码用于同一个对象而造成状态混乱,其缺点是生成的对象比较多,会浪费内存。

(2)范围(scoped):在给定的范围内,多次请求共享同一个服务对象,服务每次被请求的时候都会返回同一个对象;在不同的范围内,服务每次被请求的时候会返回不同的对象。这个范围可以由框架定义,也可以由开发人员自定义。在ASP.NET Core中,服务默认的范围是一次HTTP请求,也就是在同一次HTTP请求中,不同的注入会获得同一个对象;在不同的HTTP请求中,不同的注入会获得不同的对象。这种方式适用于在同一个范围内共享同一个对象的情况。

(3)单例(singleton):全局共享同一个服务对象。这种生命周期可以节省创建新对象的资源。为了避免并发修改等问题,单例的服务对象最好是无状态对象。

注意:不要在长生命周期的对象中引用比它短的生命周期的对象。比如不能在单例服务中引用范围服务,否则可能会导致被引用的对象已经释放或者导致内存泄漏。

获取服务(对象)的方式

调用IServiceCollection的BuildServiceProvider方法创建一个ServiceProvider对象,这个ServiceProvider对象就是一个服务定位器。由于ServiceProvider对象实现了IDisposable接口,因此需要使用using对其进行资源的释放。在我们需要获取服务的时候,可以调用ServiceProvider类的GetRequiredService方法。

            using (ServiceProvider provider = services.BuildServiceProvider())
            {
                var testService = provider.GetRequiredService<TestServiceImpl>();
                testService.Name = "Tom";
                testService.SayHi();
            }

配置系统

从配置文件中读取出字符串

使用了配置系统需要用到Microsoft.Extensions.Configuration和Microsoft.Extensions.Configuration.Json

            ConfigurationBuilder builder = new ConfigurationBuilder();

            // AddJsonFile的第二个参数为true时,如果配置文件不存在程序会报错,为false时会报错
            // 第三个参数表示配置文件被修改后是否会重新加载配置
            builder.AddJsonFile("YZK\\配置系统\\config.json", false, false);
            IConfigurationRoot root = builder.Build();  // IConfigurationRoot用来读取配置项
            string name = root["name"];

            // 访问更深层次的节点
            string address = root.GetSection("proxy:address").Value;

这种方式无法实现自动刷新。

从选项中读取出对象

使用选项方式读取配置是.NET Core中推荐的方式,因为它不仅和依赖注入机制结合得更好,而且它可以实现配置修改后自动刷新,所以使用起来更方便。

使用选项的方式读取配置需要用到Microsoft.Extensions.Options和Microsoft.Extensions.Configuration.Binder

读取配置时我们需要创建一个类用于获取注入的选项值。声明接收选项注入的对象的类型不能直接使用DbSettings、SmtpSettings,而要使用IOptions、IOptionsMonitor、IOptionsSnapshot等泛型接口类型,因为它们可以帮我们处理容器生命周期、配置刷新等。它们的区别在于,IOptions在配置改变后,我们不能读到新的值,必须重启程序才可以读到新的值;IOptionsMonitor在配置改变后,我们能读到新的值;IOptionsSnapshot也是在配置改变后,我们能读到新的值,和IOptionsMonitor不同的是,在同一个范围内IOptionsMonitor会保持一致性。IOptionsSnapshot更符合大部分场景的需求。

从命令行中读取字符串

需要安装Microsoft.Extensions.Configuration.CommandLine

实现:调用ConfigurationBuilder的AddCommandLine(args)方法

从环境变量中读取字符串

需要安装Microsoft.Extensions.Configuration.EnvironmentVariables

实现:调用ConfigurationBuilder的AddEnvironmentVariables方法或者Environment.GetEnvironmentVariable方法

日志系统

输出控制台

需要安装Microsoft.Extensions.Logging和Microsoft.Extensions.Logging.Console

            ServiceCollection services = new ServiceCollection();
            services.AddLogging(log => { log.AddConsole(); });  // 将日志服务注册到容器中
            using (var sp = services.BuildServiceProvider())
            {
                var logger =  sp.GetRequiredService<ILogger<BasicLogger>>();  // 获得服务
                logger.LogInformation("普通信息");  // 输入日志
 
            }

输出日志文件

需要安装log4net

            ILoggerRepository repository = LogManager.CreateRepository("MyRepository"); // 创建一个日志仓库
            XmlConfigurator.Configure(repository, new FileInfo("YZK/日志系统/config.xml")); // 注册,读取配置文件
            ILog log = LogManager.GetLogger(repository.Name, "MyLog");  // 获得服务
            log.Info("普通信息");   // 输出日志

第4章 Entity Framework Core基础

它是一个ORM框架,用于提高开发效率,让开发人员减少对数据库的关注,即使不会写SQL也能实现数据的持久化。它的底层是ADO.NET,通过它访问数据库。

本书的作者是提倡使用Code First模式,而我不喜欢使用它,而是使用DataBase First模式。

环境:MySQL5.7,Navicat

1、首先是新建数据库,表结构

2、安装实现了指定数据库的EF Core包,对应MySQL的包我安装的是Pomelo.EntityFrameworkCore,据说Bug比较少。版本是3.1.32,因为它依赖.NETStandard 2.0。

继续安装Pomelo.EntityFrameworkCore.MySql,版本是3.2.7。

3、使用工具生成实体类

在VS 里找到视图 > 其他窗口 > 程序包控制管理台,输入:

Scaffold-DbContext -Force "server=127.0.0.1;Port=3306;database=db_name;uid=root;pwd=123456;" -Provider "Pomelo.EntityFrameworkCore.Mysql" -OutputDir Models

Scaffold-DbContext的作用是生成DbContext的代码,表中必须要有主键,对数据库做了任何修改操作后,使用它都能快速同步到项目中。

下面是微软文档中EF -> EF Core -> 命令行参考 -> 程序包管理控制台中,Scaffold-DbContext的参数和说明:

Scaffold-DbContext

为 DbContext 生成代码,并为数据库生成实体类型。 为了让 Scaffold-DbContext 生成实体类型,数据库表必须具有主键。

参数:

参数

说明

-Connection

用于连接到数据库的连接字符串。 对于 ASP.NET Core 2.x 项目,值可以是连接字符串>的 name=。 在这种情况下,名称来自为项目设置的配置源。 这是一个位置参数,并且是必需的。

-Provider

要使用的提供程序。 通常,这是 NuGet 包的名称,例如:

Microsoft.EntityFrameworkCore.SqlServer。 这是一个位置参数,并且是必需的。

-OutputDir

要在其中放置实体类文件的目录。 路径相对于项目目录。

-ContextDir

要在其中放置 DbContext文件的目录。 路径相对于项目目录。

-Namespace

要用于所有生成的类的命名空间。 默认设置为从根命名空间和输出目录生成。 已在 EF Core 5.0 中添加。

-ContextNamespace

要用于生成的 DbContext类的命名空间。 注意:重写 -Namespace。 已在 EF Core 5.0 中添加。

-Context

要生成的 DbContext类的名称。

-Schemas

要为其生成实体类型的表的架构。 如果省略此参数,则包含所有架构。

-Tables

要为其生成实体类型的表。 如果省略此参数,则包含所有表。

-DataAnnotations

使用属性配置模型(如果可能)。 如果省略此参数,则仅使用 Fluent API。

-UseDatabaseNames

使用与数据库中显示的名称完全相同的表和列名。 如果省略此参数,数据库名称将更改为更符合 C# 名称样式约定。

-Force

覆盖现有文件。

-NoOnConfiguring

不生成 DbContext.OnConfiguring。 已在 EF Core 5.0 中添加。

-NoPluralize

请勿使用复数化程序。 已在 EF Core 5.0 中添加。

主键类型的选择并不简单

1、普通自增

自增类型的主键使用起来很简单,大部分主流数据库都支持这个功能,它有着占用磁盘空间小,可读性强,但它在数据库迁移和分布式系统(如分库分表,数据库集群)使用起来很麻烦。而且在高并发插入的时候性能比较差。

2、Guid算法

使用Guid作为主键时,虽然能保证唯一性,但会遇到性能问题,因为在使用Guid类型作为主键的时候,不能把主键设置为聚集索引。因为聚集索引是按照顺序保存主键的,在插入Guid类型主键的时候,它将会导致新插入的每条数据都要经历查找合适插入位置的过程,在数据量大的时候将会导致非常糟糕的数据插入性能。

在SQL Server中,可以设置主键为非聚集索引,但是在MySQL中,如果我们使用InnoDB引擎,那么主键是强制使用聚集索引的。

在SQL Server中,如果我们使用Guid类型(也就是uniqueidentifier类型)作为主键,一定不能把主键设置为聚集索引;在MySQL中,如果使用InnoDB引擎,并且数据插入频繁,那么一定不要用Guid类型作为主键,如果确实需要用Guid类型作为主键的话,我们只能把这个主键字段作为逻辑主键,而不是作为物理主键;

3、自增 + Guid算法

目前,还有一种主键使用策略是把自增主键和Guid结合起来使用,也就是表有两个主键(注意不是复合主键),用自增列作为物理主键,而用Guid列作为逻辑主键。物理主键是在进行表结构设计的时候把自增列设置为主键,而从表结构上我们是看不出来Guid列是主键的,但是在和其他表关联及和外部系统通信的时候(比如前端显示数据的标识的时候),我们都使用Guid列。这样不仅保证了性能,利用了Guid的优点,而且减少了主键自增导致主键值可被预测带来的安全性问题。

4、Hi/Lo算法

对于普通自增列来讲,每次获取新ID的时候都要锁定自增资源,因此在并发插入数据频繁的情况下,使用普通自增列的数据插入效率相对来讲比较低。EF Core支持使用Hi/Lo算法来优化自增列的性能。

Hi/Lo算法生成的主键值由两部分组成:高位(Hi)和低位(Lo)。高位由数据库生成,两个高位之间相隔若干个值;由程序在本地生成低位,低位的值在本地自增生成。

比如,数据库的两个高位之间相隔10,程序向数据库请求获得一个高位值50。程序在本地获取主键的时候,会首先获得Hi=50,再加上本地的Lo=0,因此主键值为50;程序再获取主键的时候,会继续使用之前获得的Hi=50,再加上本地的低位自增,Lo=1,因此主键值为51,以此类推。当Lo=9之后,再获取主键值,程序发现Hi=50的低位值已经用完了,因此就再向数据库请求一个新的高位值,数据库也许再返回一个Hi=80(因为也许Hi=60和Hi=70已经被其他服务器获取了),然后加上本地的Lo=0,最终获取主键值80,以此类推。

Hi/Lo算法的高位由服务器生成,因此保证了不同进程或者集群中不同服务器获取的高位值不会重复,而本地进程计算的低位则可以保证在本地高效率地生成主键值。

打印SQL语句

在Context类的OnConfiguring方法中,添加optionsBuilder.LogTo(Console.WriteLine)后,每次执行操作都会将SQL语句打印到控制台。

第六章 ASP.NET Core Web API基础

在.NET Framework中,ASP.NET MVC是用来进行基于视图的MVC模式开发的框架,而ASP.NET Web API 2是用来进行Web API开发的框架,这是两个不同的框架。而在ASP.NET Core中,不再做这样的区分,严格来讲,只有ASP.NET Core MVC这一个框架,ASP.NET Core MVC既支持基于视图的MVC模式开发,也支持Web API开发和Razor Pages开发等。不过在Visual Studio中创建项目的时候,仍然存在“ASP.NET Core Web API”和“ASP.NET Core应用(模型-视图-控制器)”这两种向导,分别用来创建Web API项目和传统的基于视图的MVC项目。

ASP.NET Core MVC的优点与流程

在MVC模式中,视图和控制器不直接交互、不互相依赖,彼此之间通过模型进行数据传递。使用MVC模式的优点是视图和控制器降低了耦合,系统的结构更清晰。

 浏览器端提交的请求会被封装到模型类的对象中并传递给控制器,控制器中对浏览器端的请求进行处理,然后将处理结果放到模型类的对象中传递给视图,而视图则解析模型对象,然后将其渲染成HTML内容输出给浏览器。

ASP.NET Core MVC的新工具:热重载

从.NET 6开始,.NET中增加了热重载(hot reload)功能,它允许我们在以调试方式运行程序的时候,也无须重启程序而让修改的代码生效。它的用法很简单,只要在修改完代码以后单击Visual Studio工具栏中的热重载图标,修改的代码就会立即生效。

建议的开发模式

在开发的时候,作者建议平时使用【启动(不调试)】的方式运行程序,这样在修改完代码后重新生成程序就能让修改的代码生效。在需要调试程序的时候,再以调试的方式运行程序,并且使用热重载功能来应用修改后的代码。

冥等性

是什么?

连续做相同的操作,返回的结果是相同的,例如连续两次插入相同的操作,数据库中只会插入一条数据。

如何实现?

每个请求带上唯一的标识,服务查询该标识是否存在,存在则创建一个对象,否则告知已存在。

但这种方法无法处理以下情况:

1、两次请求的频率很高,第一次请求还在判断,创建对象的过程中,第二次请求就已经来了,这时就会创建重复对象的情况。

2、分布式的环境中,两次请求可能在不同的服务器,这时对象锁,分布式事务就失效了。

解决方法是使用Redis,因为它现成、简单、易用。使用redis的incr方法可以帮我们解决这个重复创建问题。创建时,把唯一标识作为key并incr一下,并获取返回值,如果是1,那就说明没有创建过此对象,如果大于1,那就说明已经创建过了。同时key缓存时间保证对象保存到数据库即可。

返回错误码:200派与4XX派的“对决”

200派:业务逻辑的错误,如创建用户失败时,服务器会返回200状态码

理由是:对于数据库连接失败,内存不足,请求格式错误等问题返回4XX和5XX是合理的,但对于用户已存在这种业务逻辑错误,返回这种错误码,会让服务器的错误信息被淹没掉。

而且业务逻辑的错误返回200,服务器的问题返回500,这样也便于区分,减少了工作量。

4XX派:业务逻辑的错误,如创建用户失败时,服务器会返回4XX状态码

理由是:由于网关等中间件可以监测HTTP状态码,对于频繁出现的4XX和5XX错误可以发出警告,帮助运维人员及时发现问题。

RPC风格

控制器上添加的[Route("[controller]")]改为[Route("[controller]/[action]")],这样[controller]就会匹配控制器的名字,而[action]就会匹配操作方法的名字

Restful风格

1、微软提供的WebAPI控制器默认就是Restful风格

2、Get操作可以通过缓存提高访问速度,添加对于冥等操作使用PUT请求。

3、参数统一化

对于保存,更新的操作使用Post,Put请求,参数全部放到请求体中,对于查询,删除的操作使用Get,Delete请求,参数全部放到QueryString中。

为了避免打开swagger时由于方法未被[HttpGet]和[HttpPost]等标记,而报错,所以需要添加[ApiExplorerSettings(IgnoreApi = true)]标记

在ASP.NET Core Web API中,我们应该使用ActionResult来作为操作方法的返回值;如果操作方法可以声明为异步方法,那么我们就用async Task>XXX()这样的声明方式。

第七章 ASP.NET Core 基础组件

ASP.NET Core中的依赖注入

1、高频注入(基于控制器类)

实现步骤:

1.1 Startup类的ConfigureServices方法注入服务

1.2 目标控制器类中添加一个类变量,类型是注入服务;添加一个构造函数,参数是注入服务,给那个类变量赋值。

2、低频注入(基于行为方法)

2.1 Startup类的ConfigureServices方法注入服务

2.2 目标行为方法中添加一个参数,类型是注入服务,参数添加一个[FromServices]标记。

    public ActionResult<string> Login([FromServices]LoginService loginService)

注意:第一种方法使用范围更广,第二种方法适用于调用频率低,资源消耗高的情况。

配置系统与ASP.NET Core的集成

在ASP.NET Core项目中,WebApplication类的CreateBuilder方法会按照下面的顺序来提供默认的配置。.NET会按照“后面的提供者覆盖之前的提供者”的方式进行加载。

(1)加载现有的IConfiguration。

(2)加载项目根目录下的appsettings.json。

(3)加载项目根目录下的appsettings.Environment.json,其中Environment代表当前运行环境的名字,7.2.2小节将会详细介绍这一点。

(4)当程序运行在开发环境下,程序会加载“用户机密”配置。

(5)加载环境变量中的配置。

(6)加载命令行中的配置。

环境变量中的配置

在开发环境下,如图7-3所示,我们可以看到Visual Studio自动为项目的调试属性中的环境变量设置了ASPNETCORE_ENVIRONMENT=Development,这就是我们以调试模型启动项目的时候,会加载开发环境相关配置的原因。

配置文件中的配置

在测试、开发环境下,我们还可以分别再创建appsettings.Staging.json、appsettings.Production.json文件。一般来讲,我们在appsettings.json中编写开发、测试、生产环境下都共用的配置,然后在appsettings.Development.json等文件中编写开发环境等的特有配置。

用户机密

在项目中右键 - 点击【管理用户机密】后会在项目文件中添加用户机密配置,同事也会打开一个机密文件,由于这文件不存在于项目目录中,而是在当前系统的用户文件夹下,所以不会出现数据库连接字符串随着配置文件被提交到互联网上的情况。

但是由于该配置项是唯一的,多个项目用到同一个机密也需要手动修改,所以如果是团队开发,使用起来会比较麻烦。

使用配置中心可以解决上面的问题。

第八章 ASP.NET Core 高级组件

Authentication与Authorization

Authentication的音标:[ɔˌθentɪˈkeɪʃ(ə)n]

Authorization的音标:[ˌɔθərɪˈzeɪʃ(ə)n]

Authentication与Authorization区别在于中间的entication和rization

Authentication的意思是授权,验证,它是用来验证是否登录成功

Authorization的意思是授权,它是用来判断用户是否有权限访问,它应该是基于Authentication之上的。

JWT

JSON Web Token (JWT) 是一个开放标准 ( RFC 7519 ),它定义了一种紧凑且自包含的方式,用于在各方之间以 JSON 对象的形式安全传输信息。此信息可以验证和信任,因为它是数字签名的。

使用JWT的场景:

授权:这是使用 JWT 最常见的场景。用户登录后,每个后续请求都将包含 JWT,从而允许用户访问该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小并且能够在不同的域中轻松使用。

除了JWT之外还有一种鉴权授权方式Session,这是一种有状态的登录方式,而JWT是无状态登录,常用来做单点登录系统。

在日常的使用中Session有以下痛点:

在分布式应用中如果有多个后台Web服务,需要实现共享Session,增加服务器的负担。

由于Session需要配合Cookie使用,容易遭到CSRF的攻击。

如果令牌在Authorization标头中发送,则跨域资源共享 (CORS) 不会成为问题,也不会遭到CSFR攻击,因为它不使用 cookie。

下图是如何获取并使用JWT的流程图:

1、应用程序或客户端向授权服务器请求授权。

2、当授权被授予时,授权服务器向应用程序返回一个访问令牌。

3、应用程序使用访问令牌访问受保护的资源(如 API)。

参考:

(JWT中文网:JWT中文文档网)

(C#技术栈入门到精通系列19——鉴权授权IdentityServer JWT:C#技术栈入门到精通系列19——鉴权授权IdentityServer JWT - BigBox777 - 博客园)

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/178742.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++:类中const修饰的成员函数

目录 一.const修饰类的成员函数 1.问题引出&#xff1a; 代码段&#xff1a; 2.问题分析 3.const修饰类的成员函数 二. 类的两个默认的&运算符重载 三. 日期类小练习 一.const修饰类的成员函数 1.问题引出&#xff1a; 给出一段简单的代码 代码段&#xff1a; #in…

springcloud3 Sentinel的搭建以及作用

一 sentinel的概念 1.1 sentinel Sentinel是分布式系统流量控制的哨兵&#xff0c;阿里开源的一套服务容错的综合性解决方案。 主要用来处理&#xff1a; 服务降级 服务熔断 超时处理 流量控制 sentinel 的使用可以分为两个部分: 核心库&#xff08;Java 客户端&#…

【软件工程】用例图、状态图与活动图

题目要求&#xff1a; 一、投诉人对广州市燃气行业相关单位的经营和服务不满意或存在意见时&#xff0c;对燃气处或市政园林局服务监督处进行投诉。 二、燃气处投诉专管员受理直接来自投诉人或由服务监督处转来的相关投诉。 三、燃气处投诉专管员落实相关单位&#xff08;或…

DlhSoft Gantt Chart Light Library 4.3.47 Crack

DlhSoft Gantt Chart Light Library 4.3.47 改进了 Microsoft Project XML 文件的加载和图像的导出。 2023 年 1 月 24 日 - 10:09新版本 特征 改进了 Microsoft Project XML 文件的加载和从“ScheduleChartDataGrid”导出图像。 添加了新的“TotalResourceEffort”和“TotalRe…

USART 数据流控制

USART 数据流控制 也就是 USART_HardwareFlowControl 一、流控制的作用 这里讲到的 “流”&#xff0c;指的是数据流&#xff1b;在数据通信中&#xff0c;流控制是管理两个节点之间数据传输速率的过程&#xff0c;以防止出现接收端的数据缓冲区已满&#xff0c;而发送端依然继…

3.5动态规划--凸多边形的最优三角剖分

写在前面 尽管这是一个几何问题&#xff0c;但本质上与3.1-矩阵连乘极为相似 定义dp数组的含义&#xff1a;t[i][j]表述以点Vi-1&#xff0c;Vi&#xff0c;...&#xff0c;Vj为顶点的最优三角形剖分的最优权函数值 我们要计算的最优值在 t[1][n] 递归结构&#xff1a;凸多…

通过Moonbeam的Connected Contracts互连合约从Axelar转移Token至Centrifuge

将Moonbeam预编译智能合约功能与波卡指定技术交互&#xff0c;再结合Axelar通用消息传递&#xff08;GMP&#xff09;&#xff0c;能够实现其他链无法完成的独特交互。阅读本文了解Connected Contracts互连合约如何通过只与单条链交互连接Axelar的EVM链发送Token至Centrifuge等…

四、新图片、新视频预测(Datawhale组队学习)

文章目录配置环境预测新图像载入图像并进行预处理导入训练好的模型前向预测将分类结果写入原图中预测新视频导入训练好的模型视频预测单帧图像分类预测可视化方案一&#xff1a;原始图像预测结果文字可视化方案二&#xff1a;原始图像预测结果文字各类别置信度柱状图预测摄像头…

Mybatis 基本使用案例

1、基本的CRUD 1.1、新增 <!--int insertUser();--> <insert id"insertUser"> insert into t_user values(null,admin,123456,23,男) </insert> 1.2、删除 <!--int deleteUser();--> <delete id"deleteUser"> delete fro…

【docker概念和实践 4】容器命令和案例(2)

一、说明 docker的四个要素是&#xff1a;本地的Docker-engine、网上&#xff08;本地&#xff09;的仓库、镜像images、容器&#xff1b;初学者必须了解这是个概念的关系。但是&#xff0c;真正重要的概念是容器&#xff0c;因为&#xff0c;只有掌握了容器&#xff0c;才能具…

3.2主存储器的基本组成

文章目录一、引子二、半导体元件1.基本半导体元件&#xff08;1&#xff09;MOS管&#xff08;2&#xff09;电容2.读写二进制数&#xff08;1&#xff09;读出二进制①二进制1②二进制0&#xff08;2&#xff09;写入二进制3.存储体三、存储芯片的基本原理1.译码器2.控制电路3…

由Bitlocker问题引发的思考

由Bitlocker问题引发的思考一、什么是Bitlocker问题二、如何解决Bitlocker问题三、萌生的思考一、什么是Bitlocker问题 Bitlocker概述 BitLocker 驱动器加密是一项数据保护功能&#xff0c;它与操作系统集成&#xff0c;用于解决来自丢失、被盗或销毁不当的计算机的数据被盗或…

高级Spring之Bean后处理器

常见Bean后处理器的作用&#xff1a; public static void main(String[] args) {// ⬇️GenericApplicationContext 是一个【干净】的容器 干净:没有额外的添加bean工厂处理器,bean处理器,消除一些干拢GenericApplicationContext context new GenericApplicationContext();//…

(深度学习快速入门)第三章第三节3:深度学习必备组件之优化器和优化算法

文章目录一&#xff1a;优化算法&#xff08;1&#xff09;优化算法概述&#xff08;2&#xff09;梯度下降法二&#xff1a;优化器一&#xff1a;优化算法 &#xff08;1&#xff09;优化算法概述 优化算法&#xff1a;对于深度学习问题&#xff0c;我们通常会先定义损失函数…

【华为上机真题】寻找相同子串

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

2023牛客寒假算法集训营3

&#xff08;数学场真折磨人&#xff09; A. 不断减损的时间&#xff08;贪心&#xff09; 题意&#xff1a; 给定一个数组&#xff0c;任意次操作&#xff0c;每次操作可以 选择一个偶数除以 222 。 求最终数组所有元素之和的最小值。 思路&#xff1a; 要使得所有元素之…

(python)selenium工具的安装及其使用

selenium概述 一个自动化测试工具。它可以让python代码调用浏览器。并获取到浏览器中加载的各种资源 优缺点&#xff1a; 优点 selenium能够执行页面上的js&#xff0c;对于js渲染的数据和模拟登陆处理起来非常容易使用难度简单爬取速度慢&#xff0c;爬取频率更像人的行为&a…

k8s安装nfs设置pv pvc并部署mysql

在k8s系列第一篇中提到有一个用于nfs机器没有部署任何东西&#xff0c;这一篇我们来搭建nfs服务&#xff0c;并在k8s上部署mysql&#xff0c;并将mysql的data目录映射到nfs中。网上的部分教程为了简便教学用的hostPath做的映射&#xff0c;只是便于教学的简便做法&#xff0c;实…

Linux常用命令——skill命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) skill 向选定的进程发送信号冻结进程 补充说明 skill命令用于向选定的进程发送信号&#xff0c;冻结进程。这个命令初学者并不常用&#xff0c;深入之后牵涉到系统服务优化之后可能会用到。 语法 skill(选项…

vim的自动化配置(一条指令就够了)

应该没有人在因为vim中括号不能对齐和补齐和自动缩进而烦恼吧&#xff01; 自动化配置不香吗&#xff1f; 如果你想把你的vim给配置成像vs2022编译器一样&#xff0c;那么恭喜你&#xff0c;当你看到这篇文章的时候你就要成功了&#xff01; 一条指令&#xff0c;下载出vs20…