.Net Core 5.0身份验证(鉴权)

您所在的位置:网站首页 鉴权类型是什么 .Net Core 5.0身份验证(鉴权)

.Net Core 5.0身份验证(鉴权)

2023-09-02 12:24| 来源: 网络整理| 查看: 265

Asp.Net Core 5.0身份验证(鉴权) 1.身份验证(鉴权)1.1 什么是身份验证1.2 鉴权与授权的区别1.3 如何区分鉴权与授权(联想记忆法)1.4 身份验证中间件的简单使用1.4.1 名词解释1.4.2 授权相关的类与接口 1.5 实操1.5.1 目标1.5.2 环境1.5.3 开始 1.6 总结1.7实操涉及到的类:

1.身份验证(鉴权) 1.1 什么是身份验证

身份验证,也叫鉴权,故名思意就是主要的作用便是用做验证身份。

1.2 鉴权与授权的区别

鉴权与授权的概念上是有区别的,举个例子。一天周末,小明去游乐场玩。首先,向工作人员出示入场门票,工作人员验证门票的有效期,门票是否伪造…验证通过后小明进入游乐园,这时小明入园后想去玩过山车,可过山车是VIP门票才能玩的,设备管理员查看了小明的门票,发现小明的门票是普通游客,所以没法玩过山车。

鉴权:工作人员验证门票的过程 = 鉴权。 授权:小明不是VIP,没有玩过山车的权限,这就是授权。

利用上面一个例子对应到我们一个请求的过程: 小明 = 一个请求 门票 = token 检票员 = 鉴权中间件 设备管理员 = 授权中间件 过山车 = 受保护的资源

那么将上面的例子转换为一次http请求过程的话就是,请求到达鉴权中间件,鉴权中间件验证后是否验证通过,不通过,则直接返回未授权,通过后到达授权中间件,授权中间件查看是否有权限访问该资源,有则通过,否则返回权限不足。 在这里插入图片描述

1.3 如何区分鉴权与授权(联想记忆法)

鉴权的英文单词是:Authentication 授权的英文单词是:Authorization

看起来很像对吧,我也经常搞混,记得第一次写鉴权时,硬是没效果,查了半天才发现中间件用错了。😂,所以,一定不要记混了,我也就是个专科英文不好,我是这么记的。 我用的是联想记忆法。首先,找它们之间的区别,对我而言最直观的是鉴权的单词里包含en,而授权的单词包含or。而鉴权又叫身份认证,认的拼音里也包含en,所以单词里有en的就是鉴权,而另一个就是授权。

1.4 身份验证中间件的简单使用 1.4.1 名词解释

Scheme 译:方案,简单了说就是使用什么方式来验证身份,常见的有cookie、jwt web token,这里主要使用jwt讲解。

Authentication 译:授权,就是身份验证,抽象了说就是看看你有没有身份证,身份证是否伪造,是否过期等。

1.4.2 授权相关的类与接口

Claim:你身份的信息,比如:身份证上有姓名,出生日期,那么就可以这样使用

//姓名 var cmName = new Claim("Name","张三") //出生日期 var cmbirthday = new Claim("birthday", "1998.01.01", ClaimValueTypes.DateTime);

IAuthenticationHandler:身份验证处理程序,这个接口主要负责验证逻辑处理,也就是说你如果不想使用Jwt作为验证方案而想自定义验证逻辑就可以实现这个接口。主要的方法如下:

InitializeAsync(AuthenticationScheme, HttpContext) 初始化,也就是说最先执行的是这个方法。 AuthenticateAsync() 身份验证核心方法。 ChallengeAsync() 质疑当前请求,说白了就是身份验证不通过就调用这个方法,一般返回http状态码401表示身份授权失败 ForbidAsync()禁止当前请求,身份验证通过,但权限不足时调用,一般返回403表示权限不足

AuthenticateResult:

身份验证结果,如果你用到IAuthenticationHandler来实现自定义身份验证的话那么这个类是一定会用上的,使用Seccess()来表示身份验证成功,NoResult()或不返回结果都表示身份验证失败。

Jwt相关的类:

我们都知道jwt加密方式有由三部分组成,分别是header、payload、signature,至于还不知道这三个部分的作用的可以自行百度,这里就不作多赘述了),对应的三个类分别是:

JwtHeader JwtPayload JwtSecurityToken JwtBearerEvents:看到Event就知道与事件相关,通过重写该类中的各个事件对应的方法实现对验证流程的控制,各事件对应的方法如下

验证前:MessageReceived() 验证失败后:Challenge() 权限不足:Forbidden() 验证通过后:TokenValidated() 验证过程中发生异常:AuthenticationFailed()

1.5 实操 1.5.1 目标

使用Jwt身份很验证且对验证流程做控制。

1.5.2 环境

工具:vs2019 框架:Asp.Net Core 5.0

1.5.3 开始

1.先安装Microsoft.AspNetCore.Authentication.JwtBearer 在这里插入图片描述

2.准备好jwt配置文件

“JwtSetting”: { “Issuer”: “zmz.com”,//颁发token端 “Audience”: blog.console.com",//接受端 “ExpireSeconds”: 1440,//token过期时间(秒) “ENAlgorithm”: “HS256”,//加密算法 “SecurityKey”: “Zmz=Start20180621End20”//密钥,如果你是用的也是HS256算法那么密钥强制大于16位 }

3.准备Jwt配置类

/// /// jwt配置 /// public class JwtSetting { /// /// 密钥 /// public string SecurityKey { get; set; } /// /// 加密算法 /// public string ENAlgorithm { get; set; } /// /// 颁发者 /// public string Issuer { get; set; } /// /// 接收者 /// public string Audience { get; set; } /// /// 过期时间 单位:秒 /// public int ExpireSeconds { get; set; } }

4.准备生成token的方法

public class JwtHelper { public static string BuildToken(UserIdentity identity) { var jwtsetting = ConfigurationFileHelper.GetNode("JwtSetting"); //准备calims,随便写,爱写多少写多少,但千万别放敏感信息 var calims = identity.PropValuesType().Select(x => new Claim(x.Name, x.Value.ToString(),x.Type)).ToList(); //创建header var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtsetting.SecurityKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var header = new JwtHeader(creds); //创建payload var payload = new JwtPayload(jwtsetting.Issuer, jwtsetting.Audience,calims,DateTime.Now, DateTime.Now.AddMinutes(jwtsetting.ExpireSeconds)); //创建令牌 var token = new JwtSecurityToken(header,payload); return new JwtSecurityTokenHandler().WriteToken(token); } }

生成token我使用了反射来生成Claims,涉及的方法放在了文章最后

5.为了能够实现对验证流程的控制,我们先重新JwtBearerEvents内的方法,并更换token的来源

public class JwtBearerEvent : JwtBearerEvents { //自定义的http响应类 private ResponseDto ResponseDto; public JwtBearerEvent() { ResponseDto = new ResponseDto() { RequestIsSuccess = false, }; } /// /// 验证之前 /// /// /// public override Task MessageReceived(MessageReceivedContext context) { //默认情况下验证的token是从请求头中的【authorization】取得,我这里想要不使用默认的token来源,把它改成来自于请求头【access_token】,context.Token就是 var authorizationIsHave = context.Request.Headers.ContainsKey("access_token"); if (authorizationIsHave) context.Token = context.Request.Headers["access_token"].ToString(); return base.MessageReceived(context); } /// /// 验证通过但权限不足 /// /// /// public override Task Forbidden(ForbiddenContext context) { context.Response.StatusCode = 200; ResponseDto.ResponseState = ResponseState.Forbidden; ResponseDto.ErrorMsg = "Forbidden,权限不足"; context.HttpContext.ResponseJsonAsync(ResponseDto); return Task.CompletedTask; } /// /// 验证失败,如token格式不正确 /// /// /// public override Task Challenge(JwtBearerChallengeContext context) { context.Response.StatusCode = 200; ResponseDto.ResponseState = ResponseState.Unauthorized; ResponseDto.ErrorMsg = $"未经授权 Unauthorized,{context.Error}"; context.HttpContext.ResponseJsonAsync(ResponseDto); return Task.CompletedTask; } /// /// 验证通过 /// /// /// public override Task TokenValidated(TokenValidatedContext context) { base.TokenValidated(context); UserIdentity user = new UserIdentity(); var claims = context.Principal.Claims; ///自定义的用户身份类。 UserIdentity.CurrentUser = new UserIdentity() { UserId = Convert.ToInt32(claims.FirstOrDefault(x => x.Type == "UserId")?.Value), Account = claims.FirstOrDefault(x => x.Type == "Account")?.Value, Name = claims.FirstOrDefault(x => x.Type == "Name")?.Value, }; return Task.CompletedTask; } /// /// 在处理请求期间引发异常时调用 /// /// /// public override Task AuthenticationFailed(AuthenticationFailedContext context) { context.Fail(context.Exception); context.Response.StatusCode = 200; context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; ResponseDto.ResponseState = ResponseState.Unauthorized; ResponseDto.ErrorMsg = "token已过期,请重新登录"; context.HttpContext.ResponseJsonAsync(ResponseDto); return Task.CompletedTask; } }

6.Startup添加Authentication服务与中间件

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, option => { JwtSetting jwtSetting = new JwtSetting(); _configuration.Bind("JwtSetting", jwtSetting); option.SaveToken = true; option.TokenValidationParameters = new TokenValidationParameters() { ValidIssuer = jwtSetting.Issuer,//发行人 ValidAudience = jwtSetting.Audience,//订阅人 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSetting.SecurityKey)),//解密的密钥 ValidateIssuerSigningKey = true,//是否验证签名,不验证的画可以篡改数据,不安全 ValidateIssuer = true,//是否验证发行人,就是验证载荷中的Iss是否对应ValidIssuer参数 ValidateAudience = true,//是否验证订阅人,就是验证载荷中的Aud是否对应ValidAudience参数 ValidateLifetime = true,//是否验证过期时间,过期了就拒绝访问 ClockSkew = TimeSpan.Zero,//这个是缓冲过期时间,也就是说,即使我们配置了过期时间,这里也要考虑进去,过期时间+缓冲,默认好像是7分钟,你可以直接设置为0 //RequireExpirationTime = true, }; option.Events = new JwtBearerEvent(); });

记得将Events替换成我们重写的类,另外将授权的服务也加上,最后别忘了添加中间件。

app.UseAuthentication(); app.UseAuthorization();

TokenValidationParameters:token验证的参数

7.结束

最后只要将按流程走一边就可以了。

用户登录=>生成token并返回前端,添加access_token(默认情况下是authorization)将token设为其值,访问一个带有[Authorize]特性的Action。

1.6 总结

文章有点长,但其实讲的东西不多。总结一下,身份验证就是检查你身份的一个过程,这个方法由很多常见的由jwt、cookie甚至是自定义,不过万变不离其宗,核心的接口就一个 IAuthenticationHandler处理验证逻辑,无论是jwt还是什么,只要在.Net Core上提供的认证方案都必须实现这个接口,另外还遗留了一些其它的问题没有讲到,比如,我验证之后如何取得我当前用户的信息?如何退出登录?token如何续签?感兴趣的可以自行研究,另外学习一定要带着问题去学,加强深度思考的能力。

1.7实操涉及到的类:

用户身份模型

/// /// 用户身份模型 /// public class UserIdentity { /// /// 用户ID /// public int UserId { get; set; } /// /// 昵称 /// public string Name { get; set; } /// /// 账号 /// public string Account { get; set; } /// /// 当前登录用户身份模型 /// [ThreadStatic, Description("指示该值线程内唯一")] public static UserIdentity CurrentUser; }

配置文件帮助类

using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json; namespace Zmz.Tools { /// /// 配置文件帮助类 /// /// /// 读取配置文件信息 /// public class ConfigurationFileHelper { public static IConfiguration Configuration { get; set; } static ConfigurationFileHelper() { Configuration = new ConfigurationBuilder() .Add(new JsonConfigurationSource { Path = "appsettings.json", ReloadOnChange = true }) .Build(); } /// /// 获得配置文件的对象值 /// /// 文件路径 /// /// public static string GetJson(string jsonPath, string key) { if (string.IsNullOrEmpty(jsonPath) || string.IsNullOrEmpty(key)) return null; IConfiguration config = new ConfigurationBuilder().AddJsonFile(jsonPath).Build();//json文件地址 return config.GetSection(key).Value;//json某个对象 } /// /// 获取数据库连接字符串 /// /// public static string GetMysqlConnection() { return Configuration.GetConnectionString("MySql").Trim(); } /// /// 根据节点名称获取配置模型 /// /// /// /// public static T GetNode(string Node) where T : new(){ T model = Configuration.GetSection(Node).Get(); return model; } } }

获取类属性,值,属性类型

public static IEnumerable PropValuesType(this object obj) { List result = new List(); var type = obj.GetType(); var props = type.GetProperties(); foreach (var item in props) { result.Add((item.Name,item.GetValue(obj),item.PropertyType.Name)); } return result; }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3