目录
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 =>{options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(o =>{o.TokenValidationPmeters = new TokenValidationPmeters{NameClType = JwtClTypes.Name,RoleClType = JwtClTypes.Role,Valisuer = Appsettings.Issuer,ValidAudience = Appsettings.Audience,IssuerSigningKey = new SymmetricSKey(Encoding.UTF8.GetBytes(Appsettings.SecretKey)),};});
在Conure中如下:
app.UseAuthentication();
在创建的项目中新建夹,里面建立控制器,层级关系如下图所示:
public class CheckJWTFilter : ActionFilttribute { public bool IsCheck { get; set; } = true; 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(){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.){ return 9002;}else{ return 9001;}}return 200;}
在上需要通过ajax提交的方式,将输入的信息传输到api。
<script type="text/javascript">$(document).ajaxSend(function (e, jqxhr, opt) {jqxhr.setRequestHeader("Authorization", "Bearer " + sessionStorage.getItem("token"));});$(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);} });} function 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