精通.NET鉴权与授权

授权在.NET 中是指确定经过身份验证的用户是否有权访问特定资源或执行特定操作的过程。这就好比一个公司,身份验证(鉴权)是检查你是不是公司的员工,而授权则是看你这个员工有没有权限进入某个特定的办公室或者使用某台设备。

两个非常容易混淆的单词

  • 鉴权Authentication --验证用户身份的过程,即确认用户是否是他们所声称的那个人

  • 授权Authorization --看一下你有没有相应权限

鉴权使用体验

  1. 创建一个web api程序
  2. 在program.cs中使用鉴权授权中间件。builder.Services.AddAuthentication是告诉框架,如何进行鉴权,app.UseAuthentication();告诉框架,需要做鉴权。
//告诉框架,如何进行鉴权
builder.Services.AddAuthentication("Cookies")//设置使用cookie方式.AddCookie("Cookies", opt =>{//没有登录,直接跳转到/NotLoggedInopt.LoginPath = "/test/login";//没有权限,直接跳转到/NotLoggedInopt.AccessDeniedPath = "/test/NotLoggedIn";});var app = builder.Build();...
//一定注意中间件的顺序,必须先鉴权,才能授权
//告诉框架,需要做鉴权
app.UseAuthentication();
//告诉框架,需要做授权
app.UseAuthorization();app.MapControllers();
app.Run();
  1. 新建一个Controller。在Action方法上面标记[Authorize]表示使用该接口需要鉴权,也可以直接将[Authorize]特性放在Controller上面,对于不需要鉴权的Action上面放置[AllowAnonymous]
[HttpGet]
[Authorize]
public IActionResult UseCookie()
{return Ok("初步使用-使用cookie");
}[HttpGet]
public async Task<IActionResult> Login(string name,string password)
{var claimsIdentity = new ClaimsIdentity("user");claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, name));claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "user"));var principal = new ClaimsPrincipal(claimsIdentity);//ClaimsIdentity 是一个表示用户身份的对象,它是基于声明(Claims)的概念构建的。它是一种描述用户的方式,例如用户的姓名、角色、身份验证方式等信息。可以将 ClaimsIdentity 看作是一个身份容器,里面包含了一系列用于识别用户身份的声明。//ClaimsPrincipal 是一个更高级别的概念,它代表一个主体(可以是用户或者其他实体),这个主体拥有一个或多个身份(ClaimsIdentity)。可以把 ClaimsPrincipal 想象成一个包含多个身份卡片(ClaimsIdentity)的钱包,其中每个身份卡片都可以用于证明主体的某种身份。await HttpContext.SignInAsync(principal,new AuthenticationProperties{ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(30)});return Ok("登录成功");
}
  1. 未登录时直接访问/Test/UseCookie,报错,原因是未登录直接跳转到/test/login,而没有name和password参数,导致报400错误。

image-20241122100104732

  1. 使用/Test/Login?name=1&password=1登录后,调用/test/login,访问成功。

    image-20241122100330707

理解鉴权授权

常见的鉴权方式有Cookie、JWT,其实无论是什么鉴权方式,都是一个套路,其本质就是HTTP是无状态的,每次请求都是新的,服务器不知道他是不是之前的那个请求,所以鉴权授权都是分3步:

  1. 请求服务端,获取凭证
  2. 再次请求服务端,带上第一步获取到的凭证
  3. 服务端识别凭证, 判断是否允许访问

自定义鉴权

其实框架给我们封装好了很多鉴权方式,为了搞清原理,我们来个自定义鉴权。整个鉴权流程就是读取凭证—解析凭证—检验凭证----保存并向下一个管道传递。

  1. 新建一个类,继承自IAuthenticationHandler
public class CustomAuthenticationHandler : IAuthenticationHandler
{private AuthenticationScheme _AuthenticationScheme = null;private HttpContext _HttpContext = null;//初始化public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context){Console.WriteLine($"初始化自定义鉴权方法");_AuthenticationScheme = scheme;_HttpContext = context;return Task.CompletedTask;}//鉴权的核心动作  就都在这里---找到凭证-解析凭证-检测有效public Task<AuthenticateResult> AuthenticateAsync(){Console.WriteLine($"开始鉴权的核心动作-找到凭证-解析凭证-检测有效");//完全自定义方式string userInfo = _HttpContext.Request.Query["UrlToken"].ToString();if (string.IsNullOrWhiteSpace(userInfo)){return Task.FromResult(AuthenticateResult.NoResult());//没有凭证}else if ("abc".Equals(userInfo)) //这里简单检验一下凭证是否为abc{//识别信息后,要传递到下一个管道,供下一个管道使用var claimsIdentity = new ClaimsIdentity("custom");claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, "Test"));claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);return Task.FromResult<AuthenticateResult>(AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, null, _AuthenticationScheme.Name)));}else{return Task.FromResult<AuthenticateResult>(AuthenticateResult.Fail("登录凭证失败"));}}//未登录时的处理方式public Task ChallengeAsync(AuthenticationProperties? properties){Console.WriteLine($"没有登录");string redirectUri = "/test/login";_HttpContext.Response.Redirect(redirectUri);return Task.CompletedTask;}//没有权限时的处理方式public Task ForbidAsync(AuthenticationProperties? properties){Console.WriteLine($"没有权限");_HttpContext.Response.StatusCode = 403;return Task.CompletedTask;}
}
  1. 在program.cs中删除其他鉴权设定,增加以下设定
builder.Services.AddAuthentication(opt =>
{opt.AddScheme<CustomAuthenticationHandler>("custom", "custom-Demo");opt.DefaultScheme = "custom";
});
  1. 在控制器中新增一个Action来验证登录
[Authorize]
public async Task<IActionResult> CustomLogin()
{var userOrigin = base.HttpContext.User;var result = await HttpContext.AuthenticateAsync("custom");if(result.Principal is null){return Forbid("认证失败");}else{base.HttpContext.User = result.Principal;foreach (var item in result.Principal.Claims){Console.WriteLine($"{item.Type}:{item.Value}");}return Ok("登录成功");}
}

当访问/CustomLogin?UrlToken=abc时,正确访问,并打印出

image-20241122111127137

授权的使用

授权检测可以有两层,1:没有任何要求,只要登录有凭证就行。2:要求用户有相应的权限才行

授权的三个属性

  • Role角色
  • Policy策略
  • AuthenticationSchemes方案

角色Role

单角色

直接加上Roles =“Admin”

[Authorize(Roles ="Admin")]
public IActionResult RoleAdmin()
多角色

Admin和User都可以,满足一个就可以

[Authorize(Roles ="Admin,User")]
public IActionResult RoleAdmin()

如果是需要既能满足Roles ="Admin"又能满足Roles =“User”,则】

[Authorize(Roles ="Admin")]
[Authorize(Roles ="User")]
public IActionResult RoleAdmin()

注意:这里的Roles需要在生成凭证的时候使用ClaimTypes.Name的方式,而不能是new Claim(“Role”, “user”)

var claimsIdentity = new ClaimsIdentity("user");claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, name));
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "user"));

角色是最通用最简单的使用方式,但是不能满足个性化需求

策略Policy

Policy支持更灵活的自定义策略

builder.Services.AddAuthorization(opt =>
{opt.AddPolicy("AdminPolicy", config =>{config.RequireRole("Admin")//等价于  Roles=Admin.RequireUserName("abc").RequireClaim(ClaimTypes.Email)//要求有Country属性//更灵活的配置要求.RequireAssertion(context =>{return context.User.Claims.FirstOrDefault(c => c.Type.Equals(ClaimTypes.Email))?.Value.EndsWith("@qq.com") ?? false;});});});

直接使用特性标注[Authorize(Policy = "AdminPolicy")]

自定义Requirement

可以单独写一个Requirement

public class CustomRequirement : AuthorizationHandler<CustomRequirement>,IAuthorizationRequirement
{public CustomRequirement(string requiredName){if (requiredName == null){throw new ArgumentNullException(nameof(requiredName));}RequiredName = requiredName ?? "@qq.com";}private string RequiredName { get; }protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRequirement requirement){if (context.User is not null && context.User.HasClaim(c => c.Type == ClaimTypes.Email)){var emailClaimList = context.User.FindAll(c => c.Type == ClaimTypes.Email);if (emailClaimList.Any(c => c.Value.EndsWith(RequiredName))){context.Succeed(requirement);}else{context.Fail();}}return Task.CompletedTask;}
}

直接使用AddRequirements

builder.Services.AddAuthorization(opt =>
{opt.AddPolicy("CustomPolicy", config =>{config.AddRequirements(new CustomRequirement("@163.com"));});
});

使用

[HttpGet]
[Authorize(Policy = "CustomPolicy")]
public IActionResult CustomRequirement()
{return Ok("初步使用-自定义Requirement,要求使用163邮箱");
}
Policy的多条件组合

第一种方式

  • And

    直接在策略里增加即可

    policyBuilder.RequireRole("Admin")//都属于框架封装好的.RequireUserName("Admin")//Role  UserName都是最常用的.RequireClaim(ClaimTypes.Country)//只要求有Country属性.RequireClaim(ClaimTypes.DateOfBirth).RequireAssertion(context =>{return context.User.Claims.Any(c => c.Type.Equals(ClaimTypes.Country));//等价于RequireClaim}).Require....
  • Or
.RequireAssertion(context =>
{return  条件1 || 条件2 ;
});

第二种方式(推荐)

注意:这种方式的“与,或”是通过写不写context.Fail()来确定的。

  1. 结合自定义Requirement的方式,先建立一个父类
public class EmailRequirement : IAuthorizationRequirement{}
  1. 集成父类,设置条件,此处我们可以验证QQ邮箱和搜狗邮箱
  • qq邮箱
public class QQMailHandler : AuthorizationHandler<EmailRequirement>
{protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, EmailRequirement requirement){if (context.User is not null && context.User.HasClaim(c => c.Type == ClaimTypes.Email)){var emailClaimList = context.User.FindAll(c => c.Type == ClaimTypes.Email);if (emailClaimList.Any(c => c.Value.EndsWith("@qq.com"))){context.Succeed(requirement);}else{//注意,如果是或的关系,就不要写这句话,表示交给其他去处理//如果是与的关系,就要加上这句话,表示只要Fail一次,就是失败context.Fail();}}return Task.CompletedTask;}
}
  • sougou邮箱
public class SouGouMailHandler : AuthorizationHandler<EmailRequirement>
{protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, EmailRequirement requirement){if (context.User is not null && context.User.HasClaim(c => c.Type == ClaimTypes.Email)){var emailClaimList = context.User.FindAll(c => c.Type == ClaimTypes.Email);if (emailClaimList.Any(c => c.Value.EndsWith("@sougou.com"))){context.Succeed(requirement);}else{//注意,如果是或的关系,就不要写这句话,表示交给其他去处理//如果是与的关系,就要加上这句话,表示只要Fail一次,就是失败context.Fail();}}return Task.CompletedTask;}
}
  1. 在Program.cs中使用ioc进行注册
builder.Services.AddSingleton<IAuthorizationHandler,QQMailHandler>();
builder.Services.AddSingleton<IAuthorizationHandler,SouGouMailHandler>();
  1. 在策略中增加
opt.AddPolicy("OrPolicy", config =>
{//此处只需要增加父类即可config.AddRequirements(new EmailRequirement());
});
  1. 使用
[HttpGet]
[Authorize(Policy = "OrPolicy")]
public IActionResult OrRequirement()
{return Ok("初步使用-使用Requirement,要求使用QQ或者sougou邮箱");
}

多Scheme

  1. 在progress.cs中增加两个鉴权设置,一定要注意前后顺序,后面的会覆盖前面的
//告诉框架,如何进行鉴权
builder.Services.AddAuthentication(opt =>
{opt.AddScheme<CustomAuthenticationHandler>("custom", "custom-Demo");opt.DefaultScheme = "custom";
});builder.Services.AddAuthentication("Cookies")//设置使用cookie方式.AddCookie("Cookies", opt =>{//没有登录,直接跳转到/NotLoggedInopt.LoginPath = "/test/login";//没有权限,直接跳转到/NotLoggedInopt.AccessDeniedPath = "/test/NotLoggedIn";});
  1. 增加Action
[HttpGet]
[Authorize(Policy = "CountryPolicy", AuthenticationSchemes = "Cookies,UrlTokenScheme")]
public async Task<IActionResult> MultiToken()
{var r1 = await base.HttpContext.AuthenticateAsync("Cookies");var r2 = await base.HttpContext.AuthenticateAsync("custom");Console.WriteLine($"cookies:{r1?.Principal?.Claims.First().Value}");Console.WriteLine($"custom:{r2?.Principal?.Claims.First().Value}");return Ok("访问成功");
}
  1. 先登录获取cookie,/Test/Login?name=1&password=1,再访问/Test/MultiToken?UrlToken=abc这样就能得到两个Scheme的认证信息

image-20241122163858725

JWT的鉴权与授权

  1. nuget安装Microsoft.AspNetCore.Authentication.JwtBearer
  2. Program中定义
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(opt =>
{//此处配置为删减版,可根据需求自定义设定opt.TokenValidationParameters = new TokenValidationParameters{ValidateIssuer = false,//是否验证IssuerValidateAudience = false,//是否验证AudienceValidateLifetime = false,//是否验证失效时间ValidateIssuerSigningKey = true,//是否验证SecurityKey//秘钥,秘钥一般是和配置文件关联起来,此处为了方便直接写字符串IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MyTestCustomSecurityKeySymmetricSecurityKey"))};
});
  1. 在登录Action中生成一个token
public IActionResult GetToken(string name, string password)
{var claims = new[]{new Claim("Name", name),new Claim("id", "11"),new Claim(ClaimTypes.Name, "Test"),new Claim("EMail", "test@qq.com"),new Claim("Account", "Administrator"),new Claim(ClaimTypes.Role,"Admin"),new Claim("Sex", "1")};//这个地方的秘钥一般是和配置文件关联起来,此处为了方便直接写字符串var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MyTestCustomSecurityKeySymmetricSecurityKey"));//加密算法var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);var token = new JwtSecurityToken(claims: claims,expires: DateTime.Now.AddSeconds(60 * 10),//10分钟有效期signingCredentials: creds);string returnToken = new JwtSecurityTokenHandler().WriteToken(token);//返回加密的tokenreturn Ok(returnToken);
}
  1. 使用
// 最简单的使用,如果有详细的授权验证,请参考上文
[Authorize]
public IActionResult JWTTest()

请求时,需要在请求头中Authorization:中带上Bearer 生成的token,注意Bearer后面有一个空格

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Task和WebSocket使用

在现代 Web 应用中&#xff0c;WebSocket 作为一种全双工通信协议&#xff0c;为实时数据传输提供了强大的支持。若要确保 WebSocket 在生产环境中的稳定性和性能&#xff0c;使用 Nginx 作为反向代理服务器是一个明智的选择。本篇文章将带你了解如何在 Nginx 中配置 WebSocket…

机器学习任务功略

loss如果大&#xff0c;训练资料没有学好&#xff0c;此时有两个可能&#xff1a; 1.model bias太过简单&#xff08;找不到loss低的function&#xff09;。 解决办法&#xff1a;增加输入的feacture&#xff0c;设一个更大的model&#xff0c;也可以用deep learning增加弹性…

STL:相同Size大小的vector和list哪个占用空间多?

在C中&#xff0c;vector和list是两种不同的序列容器。vector底层是连续的内存&#xff0c;而list是非连续的&#xff0c;分散存储的。因此&#xff0c;vector占用的空间更多&#xff0c;因为它需要为存储的元素分配连续的内存空间。 具体占用多少空间&#xff0c;取决于它们分…

Windows 10电脑无声问题的全面解决方案

Windows 10操作系统以其强大的功能和用户友好的界面赢得了广大用户的青睐&#xff0c;但在使用过程中&#xff0c;有时会遇到电脑突然没有声音的问题。这一问题可能由多种原因引起&#xff0c;包括音频驱动程序问题、音频设置错误、系统更新冲突等。本文将详细介绍Windows 10无…

6.824/6.5840 Lab 1: Lab 3: Raft

漆昼中温柔的不像话 静守着他的遗憾啊 旧的摇椅吱吱呀呀停不下 风卷走了满院的落叶落花 ——暮色回响 完整代码见&#xff1a; https://github.com/SnowLegend-star/6.824 在完成Lab之前&#xff0c;务必把论文多读几遍&#xff0c;力求完全理解Leader选举、log日志等过程。 …

小程序-基于java+SpringBoot+Vue的养老院管理系统设计与实现

项目运行 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境&#xff1a;IDEA&#xff0c;Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境&#xff1a;Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…

YOLOv11 NCNN安卓部署

YOLOv11 NCNN安卓部署 之前自己在验证更换relu激活函数重新训练部署模型的时候&#xff0c;在使用ncnn代码推理验证效果很好&#xff0c;但是部署到安卓上cpu模式会出现大量的错误检测框&#xff0c;现已更换会官方默认的权重 前言 YOLOv11 NCNN安卓部署 目前的帧率可以稳定…

WPF_3

x名称空间的由来和作用 WPF程序中有这样的代码&#xff1a; x:Class"WpfControlLibrary1.UserControl1"<!--这是对x的使用-->xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/…

使用flex布局实现一行固定展示n个元素

前言&#xff1a; 最近在公司中完成小程序的UI设计稿时&#xff0c;遇到了布局一个问题&#xff1a;UI设计稿想实现的布局是这样的&#xff1a; 笔者第一反应就是使用flex中的justify-content: space-between;属性&#xff0c;但是使用之后发现&#xff0c;justify-content: …

Angular v19 (三):增量水合特性详解 - 什么是水合过程?有哪些应用场景?与 Qwik 相比谁更胜一筹?- 哪个技术好我就学哪个,这就是吸心大法吧

Angular在其最新版本 v19 中引入了增量水合&#xff08;Incremental Hydration&#xff09;这一特性。这一更新引发了开发者们广泛的讨论&#xff0c;特别是在优化首屏加载速度和改善用户体验方面。本文将详解水合过程的概念、增量水合的应用场景&#xff0c;以及它与类似框架如…

各类 AI API获取方法,GPT | Claude | Midjourney等

前言 在当今数字化转型的浪潮中&#xff0c;企业和开发者都面临着前所未有的技术挑战与机遇。随着ChatGPT等大语言模型的崛起&#xff0c;AI应用开发已从可选项变成了必选项。在AI应用开发中&#xff0c;成本控制是一个普遍的痛点。单是API调用费用就包含了多个维度&#xff1…

Linux:进程间通信之system V

一、共享内存 进程间通信的本质是让不同的进程看到同一份代码。 1.1 原理 第一步&#xff1a;申请公共内存 为了让不同的进程看到同一份资源&#xff0c;首先我们需要由操作系统为我们提供一个公共的内存块。 第二步&#xff1a;挂接到要通信进程的地址空间中 &#xff…

Python数组拆分(array_split())

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

微信小程序——文档下载功能分享(含代码)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

LabVIEW氢气纯化控制系统

基于LabVIEW的氢气纯化控制系统满足氢气纯化过程中对精确控制的需求&#xff0c;具备参数设置、过程监控、数据记录和报警功能&#xff0c;体现了LabVIEW在复杂工业控制系统中的应用效能。 项目背景 在众多行业中&#xff0c;尤其是石油化工和航天航空领域&#xff0c;氢气作为…

【linux】(23)对象存储服务-MinIo

MinIO 是一个高性能的对象存储服务&#xff0c;兼容 Amazon S3 API。 Docker安装MinIo 前提条件 确保您的系统已经安装了 Docker。如果还没有安装 Docker&#xff0c;可以参考 Docker 官方文档进行安装。 1. 拉取 MinIO Docker 镜像 首先&#xff0c;从 Docker Hub 拉取 Mi…

(超详细图文)PLSQL Developer 配置连接远程 Oracle 服务

1、下载配置文件 &#xff08;超详细图文详情&#xff09;Navicat 配置连接 Oracle-CSDN博客 将下载的文件解压到单独文件夹&#xff0c;如&#xff1a;D:\App\App_Java\Oracle\instantclient-basic-windows.x64-19.25.0.0.0dbru 2、配置 打开 PLSQL Developer&#xff0c;登…

【网络篇】HTTP知识

键入网址到网页显示&#xff0c;期间发生了什么&#xff1f; 浏览器第一步是解析URL&#xff0c;这样就得到了服务器名称和文件的路径名&#xff0c;然后根据这些信息生成http请求&#xff0c;通过DNS查询得到我们要请求的服务器地址&#xff0c;然后添加TCP头、IP头以及MAC头&…

C 语言学习的经典书籍有哪些?

学习C语言的理由 C语言是一种程席设计语言&#xff0c;它是由美国AT&T公司贝尔实验室的Dennis Ritchie于1972年发明的。C语言之所以流行&#xff0c;是因为它简单易用。学习C语言的几个理由如下&#xff1a; (1)C、C#和Java使用一种被称为面向对象程序设计(0bject-Orient…

webrtc ios h264 硬编解码

webrtc ios h264 硬编解码 一 ios 系统支持 从ios8开始&#xff0c;苹果公司开放了硬解码和硬编码API&#xff08;即 VideoToolbox.framework API&#xff09; 二 主要api 1 主要解码函数 VTDecompressionSessionCreate // 创建解码 session VTDecompressionSession…