主页>技术社区>IT 技术>编程开发>C#

.Net Core 集成JWT授权验证 从页面到API详解

eIT.com.cn 2022/12/10 6:40:53 阅读 51 次

打印


.Net Core 集成JWT授权验证 从到API详解

目录

1.什么是JWT

JSON Web Token(JWT)是目前最流行的跨域身份验证案。
JWT的官网地址:.
通俗来讲,JWT定义了一种紧凑的、自包含的方式,可以将各方之信息作为JSON对象安全的传输,它是代表身份的象征令牌,可以在api接口中校验的身份以确认是否有访问api的权限。

2.JWT的使用场景

授权: 需要实现单点的时候可以使用,例如在多系统共存的环境下,在一处后,就不用在其他系统中,一次能得到其他系统的信任。使用JWT授权成功后会返回token值给当前,访问其他的模块的时候携带该token进行请求,当token过期或被纂改,则不允许访问。
信息交换: JWT是服务器、客户端之间安全的进行传输信息的好方式。因为在颁发JWT的时候可以对JWT进行签名,多方之间可以通过约定好的加密秘钥进行数据解析。

3.JWT较之Session认证的区别

Session认证

1、向服务器发送和密码。2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如角色、时间等等。3、服务器向返回 session_id,写入的 Cookie。4、随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。5、服务器收到 session_id,找到前期保存的数据,由此得知的身份。

基于token的鉴权机制

1、使用密码来请求服务器。2、服务器进行验证的信息。3、服务器通过验证颁发给token。4、客户端存储token,并在每次请求时附送上这个token值。5、服务端验证token值,并返回数据。

基于Session认证模式的问题在于,扩展性不好,单机使用没有问题,如果使用到了服务器集群或者是跨域的服务导向架构,就会需要每台服务器都要能够读取到session。例如像阿里巴巴这样的网站,在网站的背后是成百上千的子系统,一次操作或交易可能涉及到几十个子系统的协作,如果每个子系统都需要认证,不仅会疯掉,各子系统也会为这种重复认证授权的逻辑搞疯掉。用这种方案架构看上去清晰,但是工程量大。
而另外一种方案是不在服务器保存session数据,将数据保存在客户端,在发起的每次请求的时候都带到服务器,它不需要在服务端保留的会话状态信息,不用考虑在哪一台服务器进行,为应用的扩展提供了便利。但是JWT在Payload里面包含了,占用的空间比Session大,在http传输的过程中会造成影响。所以在设计的时候不要在JWT中存储太多的cl,避免发生巨大的请求。

4.JWT的结构

令牌由三部分组成,这些部分由 (.)分隔开,分别是:

  • :算法和代币类型
  • 有效载荷:数据
  • 验证签名

因此,JWT通常是这种形式使用 qqqqq.wwwww.eeeee
: 通常有两部分组成,令牌类型和使用的签名算法。

{  "alg": "HS256","typ": "JWT"}

有效载荷: payload部分是json对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号
    除了官方定义的字段,还可以个部分定义自己的私有字段。

验证签名: 签名是将第一部分(header)、第二部分(payload)、密钥(key)通过指定算法(HMAC、RSA)进行加密的。
详细可看下图:

  • 应用程序向授权服务器请求授权。
  • 校验身份,校验成功,返回token。
  • 应用程序使用访问令牌访问受保护的资源。

5.Asp.Net Core 集成JWT

开发工具:vs2019

首先新建asp.net core的web项目,项目版本选择3.1及以上。

{  "Logging": {"LogLevel": {  "Default": "@R_493_7@ion",  "Microsoft": "Warning",  "Microsoft.Hosting.Lifetime": "@R_493_7@ion"}  },  "AllowedHosts": "*",  "JwtSettings": {"Issuer": "https://localhost:51945","Audience": "https://localhost:51945","SecretKey": "Hello-key----------"  },  "TokenValidMinutes": "1",  "TokenCacheMinutes": "5"}Issuer是签发人,Audience是受众,使用者、信息传播的接受者。SecretKey是定义的秘钥。TokenValidMinutes是自己定义的有效分钟数,TokenCacheMinutes是自己定义的缓存分钟数,

然后去StartUp类中配置。
在ConureServices中如下:

//身份验证services.AddAuthentication(options =>{//认证middleware配置options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(o =>{//jwt token参数设置o.TokenValidationPmeters = new TokenValidationPmeters{NameClType = JwtClTypes.Name,RoleClType = JwtClTypes.Role,//Token颁发机构Valisuer = Appsettings.Issuer,//颁发给谁ValidAudience = Appsettings.Audience,//这里的key要进行加密IssuerSigningKey = new SymmetricSKey(Encoding.UTF8.GetBytes(Appsettings.SecretKey)),};});

在Conure中如下:

app.UseAuthentication();

在创建的项目中新建夹,里面建立控制器,层级关系如下图所示:

 public class CheckJWTFilter : ActionFilttribute { /// <summary> /// 检查是否登陆 /// </summary> public bool IsCheck { get; set; } = true; /// <summary> /// 检查颁发的令牌 /// </summary> /// <pm name="context"></pm> public override void OnActionExecuting(ActionExecutingContext filterContext) { bool igeCheckSession = filterContext.ActionDescriptor.FilterDescriptors .Select(f => f.Filter) .OfType<TypeFilttribute>() .Any(f => f.ImplementationType.Equals(typeof(IgeCheckJWTFilter))); if (igeCheckSession) { base.OnActionExecuting(filterContext); return; } if (IsCheck) { var clIdentity = (ClaIdentity)filterContext.HttpContext.User.Identity; if (clIdentity.Cla.Count() > 0 && JWTUtil.ValidateLogin(clIdentity)) { base.OnActionExecuting(filterContext); } else { filterContext.Result = new UnauthorizedResult(); } } else { base.OnActionExecuting(filterContext); } } }

接下来在Controllers夹中新建HomeController,创建简单的,上包含文本框和几个简单的按钮即可。

<input type="text" id="name" /><input type="text" id="pwd" /><button onclick="btnLogin()"></button><button onclick="ut()"></button><button onclick="getUserInfo()">信息</button>

然后再创建User控制器,用于接收提交过来的请求。如下:

[Route("GetToken")][HttpGet]public IActionResult GetToken(string data){Loginl model = JsonConvert.DeserializeObject<Loginl>(data);model.Id = "1";model.Phone = "138****8521";model.Password = "123";ResponseResult responseResult = new ResponseResult();responseResult.Success = true;responseResult.Data = JWTUtil.GetToken(model);return Ok(responseResult);}[Route("GetUserInfo")][HttpGet][CheckJWTFilter]public IActionResult GetUserInfo(){//当前请求的信息,包含token信息var clIdentity = (ClaIdentity)HttpContext.User.Identity;string name = clIdentity.FindFt(JwtClTypes.Name).Value;string phoneNumber = clIdentity.FindFt(JwtClTypes.PhoneNumber).Value;string expirationTimeStamp = clIdentity.FindFt(JwtClTypes.Expiration).Value;DateTime expiration = DateTimeUtil.Unix2Datetime(Convert.ToInt64(expirationTimeStamp));int code = GetStatusCode(expiration, Appsettings.TokenCacheMinutes);return new JsonResult(new ResponseResult() {  Data = name + "资料" });}[CheckJWTFilter][HttpGet][Route("RefreshToken")]public IActionResult RefreshToken(){string token = HttpContext.Request.Headers["Authorization"].ToString();string[] tokenArray = token.Split(' ', StringSplitOptions.RemoveEmptyEntries);var clIdentity = (ClaIdentity)HttpContext.User.Identity;string expirationTimeStamp = clIdentity.FindFt(JwtClTypes.Expiration)?.Value;if (string.IsNullOrEmpty(expirationTimeStamp)){return new JsonResult(new ResponseResult() { Success = false, Data = tokenArray.Length > 1 ? tokenArray[1] : tokenArray[0] });}DateTime expiration = DateTimeUtil.Unix2Datetime(Convert.ToInt64(expirationTimeStamp));if (DateTime. > expiration && DateTime. <= expiration.AddMinutes(Appsettings.TokenCacheMinutes)){ResponseResult responseResult = new ResponseResult();responseResult.Success = true;responseResult.Data = JWTUtil.GetToken(clIdentity);return Ok(responseResult);}return new JsonResult(new ResponseResult() { Success = true, Data = tokenArray.Length > 1 ? tokenArray[1] : tokenArray[0], Code = 200 });}private int GetStatusCode(DateTime expiration, int tokenCacheMinutes){if (expiration < DateTime.){if (expiration.AddMinutes(tokenCacheMinutes) < DateTime.){  //token已失效+不在缓冲期内  return 9002;}else{  //token已失效+在缓冲期内  return 9001;}}return 200;}

在上需要通过ajax提交的方式,将输入的信息传输到api。

<script type="text/javascript">//规定ajax请求即将发送时运行的$(document).ajaxSend(function (e, jqxhr, opt) {//统一为ajax请求Headerjqxhr.setRequestHeader("Authorization", "Bearer " + sessionStorage.getItem("token"));});//规定ajax请求成功完成时运行的$(document).ajaxSuccess(function (event, opt) {if (jqxhr.responseJSON.Success) {handleStatusCode(jqxhr.responseJSON.Code);}});		//点击登陆function btnLogin() {let name = $("#name").val();let pwd = $("#pwd").val();$.ajax({type: "GET",url: "/User/GetToken",contentType: "application/json",data: {  data: JSON.stringify({  loginName: name,  password: pwd  })}}).success(function (data) {if (data.Success) {  sessionStorage.setItem("token", data.Data);} });}		//登出 清除tokenfunction ut() {sessionStorage.removeItem("token");}		//信息function getUserInfo() {$.ajax({type: "get",url: "/User/GetUserInfo"}).done(function (data) {if (data.Success) {  console.log(data.Data);} else {  alert(data.ErrorMsg);}});}</script>

我们启动程序,模拟一下应用场景。


资料是空的,因为我们在token的时候,没有将文本框内的数据存储到有效载荷中去,所以我们资料接口时,根据Authorization传递过去的token参数进行解析,也是拿不到数据的。

接下来传递文本框数据再次进行解析,可以很清晰的从返回值中看到api能够从我们传递过去的token中解析出。

小结

使用JWT控制接口和服务器资源的访问,需要在接口上特性,表示需要有校验通过可用的令牌才能访问,这里只是简单的演示了一下jwt以及解析jwt,后期实战进阶的时候有很多地方需要补充以及完善。
例如token失效过期后的客户端的响应、如何强制在token未过期时让客户端的token失效、如何无刷新交换新的token等等。

总结

以上是为你收集整理的全部内容。

如果觉得网站内容还不错,欢迎将推荐给好友。

原文地址:https://blog.csdn.net/weixin_42794881






相关内容


热门栏目


特别声明


最新资讯
热讯排行



合作媒体友情链接
生活常识小贴士 软件开发教程 智慧城市生活网 息县通生活服务[移动版] 息县商圈[移动版] 美食菜谱
健康养生 法律知识 科技频道 电影影讯 留学考研学习 星座生肖|解梦说梦




关于我们 | 联系我们 | 合作媒体 | 使用条款 | 隐私权声明 | 版权声明

      Copyright © 2023 eIT.com.cn. All Rights Reserved. 豫ICP备2022012332号