目录
章节
第一章:.NET5从零基础到精通:全面掌握.NET5开发技能【第一章】_西瓜程序猿的博客-CSDN博客
十三、权限验证
13.1-基于Seesion/Cookies的权限认证
为了拦截一些操作:
- 传统的授权方式:Seesion、Cookies来完成:
-
- 在请求某个Action之前来做校验,验证当前操作者是否登录过,登录过就有权限。
- 如果没有权限就调到到登录页面去。
- AOP-Filter、ActionResult
13.1.1-传统的登录需要匿名
当注册全局权限验证码的时候,需要将Login取消权限验证,不然会产生这个错误:
解决:
(1)在【CustomActionAuthrizaFilterAttribute】中写代码写上这个代码支持匿名,继承自Attribute,实现IActionFilter接口。
(2)在Login的Action写上这个特性。
13.2-基于鉴权授权
//通过中间件来支持;
(1)在【Startup.cs】中创建的【Configure】方法中进行中间件,使用中间件使用在App.UseRouting()之后,在app.UseEndpoints()之前。
#region 第一步:告诉框架说我要使用鉴权授权功能 app.UseAuthentication();//鉴权:检测有没有登录,登录的是谁,赋值给User app.UseAuthorization();//授权:检测有没有权限,是否能够访问后续的页面 #endregion
(2)在【Startup.cs】中创建的【ConfigureServices】方法中增加一个AddAuthentication。
#region 增加一个授权AddAuthentication //用Cookie services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => { //如果授权是被,就跳转到这个路径中 options.LoginPath = new PathString("Eighth/Login"); }); #endregion
(3)指定哪些Action需要做鉴权授权,直接标记特性;可以标记在控制器、Action,也可以标记在全局。
(4)在控制器的Login登录方法中修改如下:
#region 鉴权:鉴权,检测有没有登录,登录的是谁,赋值给User //rolelist 是登录成功后用户的角色---是来自于数据库的查询;不同的用户会查询出不同的角色; var rolelist = new List<string>() { "Admin", "Teacher", "Student" }; //ClaimTypes.Role就是做权限认证的标识; var claims = new List<Claim>()//鉴别你是谁,相关信息 { new Claim(ClaimTypes.Role,"Admin"), new Claim(ClaimTypes.Name,name), new Claim("password",password),//可以写入任意数据 new Claim("Account","Administrator"), new Claim("role","admin"), new Claim("admin","admin"), }; foreach (var role in rolelist) { claims.Add(new Claim(ClaimTypes.Role, role)); } //将用户放在ClaimsPrincipal里面去了;相当身份证,将信息写到身份证里面去; ClaimsPrincipal userPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Customer")); HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, userPrincipal, new AuthenticationProperties { ExpiresUtc = DateTime.UtcNow.AddMinutes(30),//过期时间:30分钟 }).Wait(); #endregion
(5)当注册到控制器上、或者注册到全局上,又会出现这样的问题。
解决:在Login的Action上加上匿名特性,这个匿名是来自于框架的;
[AllowAnonymousAttribute] //匿名
13.3-鉴权授权-角色授权(目前看是将角色定义死了)
//不同的用户,可能会存在不同的角色,然而不同的角色在访问不同的页面的时候,需要做不同的拦截。——角色授权其实就是通过角色不同,做不通的权限拦截。
1.保证上一个(13.2)代码是不变。
2.在Action需要加上对应拥有访问该视图的角色。
//当访问【Index03】时,会被拦截,跳转到拦截页面:
13.4-鉴权授权-策略授权
//之前的角色授权是在代码中把【角色】定义死了,我们更希望能够自己来完成校验逻辑:
(1)创建一个类为【CustomAuthorizationHandler】,这个类专用来检验逻辑的,要求继承自【AuthorizationHandler<>】泛型类(泛型类中要求实现【IAuthorizationRequirement】接口:见步骤2)然后在CustomAuthorizationHandler类中实现【HandleRequirementAsync】抽象方法,写逻辑。
public class CustomAuthorizationHandler : AuthorizationHandler<CustomAuthorizationRequirement> { public CustomAuthorizationHandler() { } /// <summary> /// 实现抽象方法 /// </summary> /// <param name="context"></param> /// <param name="requirement"></param> /// <returns></returns> protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomAuthorizationRequirement requirement) { if (requirement.Name == "Policy01") { //策略1的逻辑 } if (requirement.Name == "Policy02") { //策略2的逻辑 } if (requirement.Name == "Policy03") { //策略3的逻辑 } if (true) { } //在这里可以定义自己的规则 { /* 其实这里可以去数据库里面做一些查询,然后根据用户的信息,做计算:如果符合就context.Succd(requirement) 否则就Task。CompletedTask */ } //context.User 鉴权成功(登录成功后),用户的信息 var role = context.User.FindFirst(u => u.Value.Contains("admin")); if (role != null) { context.Succeed(requirement);//验证通过了 } return Task.CompletedTask;//验证不通过 } }
(2)创建一个为类【CustomAuthorizationRequirement】,改类实现【IAuthorizationRequirement】接口,用来放在AuthorizationHandler<>泛型类中。
(3)怎么让其生效?在【Startup.cs】中创建的【ConfigureServices】方法中注册支持策略授权,也可以支持多种策略认证。
#region 支持多种策略认证 services.AddAuthorization(options => { options.AddPolicy("customPolicy", policy => { policy.AddRequirements(new CustomAuthorizationRequirement("Policy01")); }); }); services.AddAuthorization(options => { options.AddPolicy("customPolicy01", policy => { policy.AddRequirements(new CustomAuthorizationRequirement("Policy02")); }); }); services.AddAuthorization(options => { options.AddPolicy("customPolicy02", policy => { policy.AddRequirements(new CustomAuthorizationRequirement("Policy03")); }); }); #endregion //注册支持策略授权 services.AddSingleton<IAuthorizationHandler, CustomAuthorizationHandler>();
十四、跨平台本质
- web应用程序是一个控制台;——Main程序的入口
- 跨平台的主要原因:
跨平台的原因在于框架已经内置了一个主机,只要是程序启动,就是启动了主机,就可以监听端口;请求来了,只要是请求这个端口,主机就可以相应,所以无论是Windows还是在Linux下开发,都是要第一步完成环境的;这样就不同拘泥于在Windows还是Linux上。
十五、中间件
15.1-什么是中间件?
- 中间件的执行,是一个俄罗斯套娃;
- 先Use先执行,每一次Use一个中间件,其实就是在之前的基础上,套了一层;
- 请求来了以后,真正执行的时候,是一层一层的内部执行,在执行出来。
增加程序的扩展性:
如果想要增加一层,直接增加一个中间件就可以来完成。
15.2-常用的中间件
15.2.1-app.Run:中断式,只要使用当前中间件,后面的中间件都不执行;
15.2.2-app.Map:判断路径中包含什么内容。
15.2.3-app.MapWhen:判断式,两个委托,第一个委托做为判断条件内容,第二个委托,是要执行的逻辑
15.3-中间件扩展-引用
(1)中间件的内容可以独立开,放在一个独立的类中,需要有一定的规则如下(创建了3个中间件):
/// <summary> /// 要求构造函数带有RequestDelegate参数类型——目的是为了得到下一个中间件; /// 必须包含async Task Invoke方法,方法参数为HttpContext /// </summary> public class FirstMiddleware { private readonly RequestDelegate _next; /// <summary> /// 构造函数 /// </summary> /// <param name="next"></param> public FirstMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { await context.Response.WriteAsync($"<h1 style='color:red'>{nameof(FirstMiddleware)}——Hello Word Start</h1>"); await _next(context); await context.Response.WriteAsync($"<h2 style='color:blue'>{nameof(FirstMiddleware)}——Hello Word End</h1>"); } }
(2)在【Startup.cs】中创建的【Configure】方法中通过以下方法使用中间件:
十六、EF Core
16.1-EF Core可以做什么?
通过实体和数据库的映射,可以通过实体的操作完成对数据的操作。
映射:
- 从数据库到代码视图的映射
- 从代码到数据库的映射
16.2-DB First(数据库优先)
DB First:现有数据库,然后通过映射得到视图(和数据库的表对应);
(1)创建一个控制台应用程序,用来操作并测试。
(2)管理Nuget包,安装对应需要的包。
Install-Package Microsoft.EntityFramewrorkCore Install-Package Microsoft.Entit3FramewrorkCore.Sqlserver Install-Package Microsoft.EntityFramewrorkCore.Tools 工具--Nuget包管理器--程序包管理器控制台-命令执行(建议使用这种): Scaffold-DbContext "Data Source=.;Initial Catalog=CovidAPI;User ID=sa;Password=3344520" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entity -Force -Context CovidAPIDbContext -ContextDir / --可以简写为: Scaffold-DbContext "Data Source=.;database=CovidAPI;uid=sa;pwd=3344520" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entity 命令参数: -0utputDir 实体文件所存放的文件目录 -ContextDir DbContext文件存放的目录 -Context DbContext文件名 -Schemas 需要生成实体数据的数据表所在的模式 -Tables 等需要生成实体数据的数据表的集合 -DataArnotations -UseDatabaseNames 直接使用数据库中的表名和列名(某些版本不支持) -Force 强制执行,重写已经存在的实体文件
(3)将【EFCore.DbFirst】设置为启动项目,并在【程序包管理器控制台】中选中摸默认项目为【EFCore.DbFirst】,然后执行以上命令:
//命令执行完毕,然后会根据数据库中的表生成以下实体和DbContext:
16.3-Code First(代码优先-迁移)
迁移:代码优先,先有代码再有数据库;数据库随着业务变化迁移改变。
迁移命令:
(1)可以通过EFCore代码有的API从代码生成数据库。
(2)可以通过迁移命令生成数据库。
2.1-需要引入程序包
2.2-将【EFCore.CodeFirst】设置为启动项目,并在【程序包管理器控制台】中选中摸默认项目为【EFCore.CodeFirst】,然后执行对应的命令:
//然后可以看到生成了迁移文件和快照文件。
16.4-EFCore抓取SQL语句
16.4.1-日志输出
(1)打开Nuget包安装日志输出包。
(2)在数据上下文对象中的【OnConfiguring】方法中进行配置。
16.4.2-SQL Srever Profiler工具
(1)在SQL Srever数据库中【工具】——>【SQL Server Profiler】。
(2)打开之后进行数据库连接,和服务器是同一个服务器。
(3)选择模板和事件。
16.5-EFCore-LinqQuery(LINQ查询)
#region 其他查询 using (CovidAPIDbContext context = new CovidAPIDbContext()) { //Lambda查询 { var idlist = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 134, 46, 23, 46, 34 };//in查询 var list = context.Employees.Where(u => idlist.Contains(u.Id));//in查询 foreach (var item in list) { Console.WriteLine(item.Name); } } //LINQ查询 { var list = from u in context.Employees where new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }.Contains(u.Id) select u; foreach (var item in list) { Console.WriteLine(item.Name); } } //一般用于分页:Skip、Take——跳过第一条2,取2条 { //var list = (from u in context.Employees // where new int[] { 1, 2, 3, 4, 5, 6, 7, 7 }.Contains(u.Id) // orderby u.Id // select new // { // Name = u.Name, // Img = u.PictureUrl // }).Skip(1).Take(2); //foreach (var item in list) //{ // Console.WriteLine(item.Name); //} } //多条件 { var list = context.Employees.Where(u => u.Name.StartsWith("三") && u.Name.EndsWith("三哈")) .Where(u => u.Name.StartsWith("张")) .Where(u => u.Name.Length < 5) .OrderBy(u => u.Id); foreach (var item in list) { Console.WriteLine(item.Name); } } //连接查询 { var list = (from u in context.Employees join c in context.Departments on u.DepartmentId equals c.Id //注意:条件不能写 "==" 等于号,要使用equals where new int[] { 1, 2, 3, 4, 5, 6, 7 }.Contains(u.Id) select new { Id = u.Id, Nmae = u.Name, Bumen = c.Name }).OrderBy(u => u.Id); foreach (var item in list) { Console.WriteLine($"{item.Id}---{item.Nmae}---{item.Bumen}"); } } //左连接 //没有右连接,如果要做右连接,就把顺序调换一下就行了 { var list = (from u in context.Employees join c in context.Departments on u.DepartmentId equals c.Id into ucList from uc in ucList.DefaultIfEmpty() where new int[] { 1, 2, 3, 4, 5, 6 }.Contains(u.Id) select new { Id = u.Id, Nmae = u.Name, Bumen = uc.Name }).OrderBy(u => u.Id); foreach (var item in list) { Console.WriteLine($"{item.Id}---{item.Nmae}---{item.Bumen}"); } } #endregion }
16.5-EFCore-执行查询、修改SQL语句
#region 如果遇到非常复杂的查询——建议直接写SQL语句 using (CovidAPIDbContext context = new CovidAPIDbContext()) { //查询 { try { string sql1 = @"select * from dbo.Employees where Id > @Id"; SqlParameter parameter1 = new SqlParameter("@Id", 4); var query = context.Employees.FromSqlRaw<Employee>(sql1, parameter1); foreach (var item in query) { Console.WriteLine(item.Name); } } catch (Exception ex) { Console.WriteLine(ex.Message); throw; } } //修改 { string sql2 = @"update dbo.Employees set Name ='修改update' where id=@id"; SqlParameter parameter2 = new SqlParameter("@id", 4); int flg = context.Database.ExecuteSqlRaw(sql2, parameter2); } } #endregion
16.6-EFCore-State(动态跟踪)
增删改的动作是统一由SavaChanges以后才落实到数据库中去的:
数据库的增删改动作都是统一由SavaChanges之后,统一提交到数据库,是通过状态跟踪,任何一个增删改查的操作都会记录一个状态在内存中,增删改查的状态,一旦SavaChanges,就根据状态落实到数据库中去。
// 摘要:当前这个实体没有被上下文所跟踪 Detached=0, // 摘要:实体正在被上下文跟踪,并且存在于数据库中,他的数据库中的数据没有被更改。 Unchanged=1, // 摘要:实体被上下文跟踪,并且存在数据库中,并已标记从数据库中删除,并还没有删除。 Deleted=2, // 摘要:实体被上下文跟踪,部分或他的所有属性都已经被修改。 Modified=3, // 摘要:实体被上下文跟踪,但在数据库中还不存在。 Added=4
//注意:
状态跟踪实现了增删改便捷,但是也会有性能消耗;因为每次都要去和内存的副本的状态去做比较的。
16.7-EFCore-事务(调优小技巧)
SavaChanges就是保证事务的,多个对于数据库的操作,统一SavaChanges,就是开启了一个事务的。
EF Core自带的两个事务:
(1)SavaChanges
(2)IDbContextTransaction
16.8-EFCore-调优小技巧
(1)尽量不要进行Tolist();
(2)尽量不要使用FirstOrDefault,要使用Find进行查询。
(3)只做查询时,不需要进行增删改,就去掉状态跟踪。
//全局取消
十七、EFCore整合-分层架构
17.1-分层架构
- 没有分层的缺点:
-
- 职责不清晰
- 如果有一处修改,可能会导致需要重新修改,需要重新测试。
- 分层的优点:
-
- 职责更清晰
- 需求的变更不同修改全部代码
- 人员更好调配——让更专业的人做专业的事。
- 三层架构:
- UI:展示给用户,视图层
- BLL:业务逻辑层
- DAL:数据访问层
//分层以后:要求不能跨层调用,UI层——BLL层——DAL层
17.2-依赖注入
支持依赖注入:必须有抽象,依赖抽象而不是依赖实现细节。
上一章节:.NET5从零基础到精通:全面掌握.NET5开发技能【第二篇】_西瓜程序猿的博客-CSDN博客
版权声明:本文为原创文章,版权归 [西瓜程序猿] 所有,转载请注明出处,有任何疑问请私信咨询。