加入收藏 | 设为首页 | 会员中心 | 我要投稿 南平站长网 (https://www.0599zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > Asp教程 > 正文

详解ASP.NET Core Web Api之JWT刷新Token

发布时间:2020-11-23 13:35:51 所属栏目:Asp教程 来源:网络整理
导读:副标题#e# 如题,本节我们进入JWT最后一节内容,JWT本质上就是从身份认证服务器获取访问令牌,继而对于用户后续可访问受保护资源,但是关键问题是:访问令牌的生命周期到底设置成多久呢?见过一些使用JWT的童鞋会将JWT过期时间设置成很长,有的几个小时,有

//模拟登陆 $('#btn').click(function () { GetTokenAndRefreshToken(); }); //获取Token function GetTokenAndRefreshToken() { $.post('http://localhost:5000/api/account/login').done(function (data) { saveAccessToken(data.accessToken); saveRefreshToken(data.refreshToken); }); }

//从localStorage获取AccessToken function getAccessToken() { return localStorage.getItem('accessToken'); } //从localStorage获取RefreshToken function getRefreshToken() { return localStorage.getItem('refreshToken'); } //保存AccessToken到localStorage function saveAccessToken(token) { localStorage.setItem('accessToken', token); } //保存RefreshToken到localStorage function saveRefreshToken(refreshToken) { localStorage.setItem('refreshToken', refreshToken); }

此时我们再来点击【调用客户端获取当前时间】,同时将登录返回的访问令牌设置到请求头中,代码如下:

$('#btn-currentTime').click(function () { GetCurrentTime(); }); //调用客户端获取当前时间 function GetCurrentTime() { $.ajax({ type: 'get', contentType: 'application/json', url: 'http://localhost:5001/api/home', beforeSend: function (xhr) { xhr.setRequestHeader('Authorization', 'Bearer ' + getAccessToken()); }, success: function (data) { alert(data); }, error: function (xhr) { } }); }

客户端请求接口很简单,为了让大家一步步看明白,我也给出来,如下:

[Authorize] [HttpGet("api/[controller]")] public string GetCurrentTime() { return DateTime.Now.ToString("yyyy-MM-dd"); }

好了到了这里我们已经实现模拟登录获取访问令牌,并能够调用客户端接口获取到当前时间,同时我们也只是返回了刷新令牌并存储到了本地localStorage中,并未用到。当访问令牌过期后我们需要通过访问令牌和刷新令牌去获取新的访问令牌,对吧。那么问题来了。我们怎么知道访问令牌已经过期了呢?这是其一,其二是为何要发送旧的访问令牌去获取新的访问令牌呢?直接通过刷新令牌去换取不行吗?有问题是好的,就怕没有任何思考,我们一一来解答。我们在客户端添加JWT中间件时,里面有一个事件可以捕捉到访问令牌已过期(关于客户端配置JWT中间件第一节已讲过,这里不再啰嗦),如下:

options.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) { context.Response.Headers.Add("act", "expired"); } return Task.CompletedTask; } };

通过如上事件并捕捉访问令牌过期异常,这里我们在响应头添加了一个自定义键act,值为expired,因为一个401只能反映未授权,并不能代表访问令牌已过期。当我们在第一张图中点击【调用客户端获取当前时间】发出Ajax请求时,如果访问令牌过期,此时在Ajax请求中的error方法中捕捉到,我们在如上已给出发出Ajax请求的error方法中继续进行如下补充:

error: function (xhr) { if (xhr.status === 401 && xhr.getResponseHeader('act') === 'expired') { // 访问令牌肯定已过期 } }

到了这里我们已经解决如何捕捉到访问令牌已过期的问题,接下来我们需要做的则是获取刷新令牌,直接通过刷新令牌换取新的访问令牌也并非不可,只不过还是为了安全性考虑,我们加上旧的访问令牌。接下来我们发出Ajax请求获取刷新令牌,如下:

//获取刷新Token function GetRefreshToken(func) { var model = { accessToken: getAccessToken(), refreshToken: getRefreshToken() }; $.ajax({ type: "POST", contentType: "application/json; charset=utf-8", url: 'http://localhost:5000/api/account/refresh-token', dataType: "json", data: JSON.stringify(model), success: function (data) { if (!data.accessToken && !data.refreshToken) { // 跳转至登录 } else { saveAccessToken(data.accessToken); saveRefreshToken(data.refreshToken); func(); } } }); }

发出Ajax请求获取刷新令牌的方法我们传入了一个函数,这个函数则是上一次调用接口访问令牌过期的请求,点击【调用客户端获取当前时间】按钮的Ajax请求error方法中,最终演变成如下这般:

error: function (xhr) { if (xhr.status === 401 && xhr.getResponseHeader('act') === 'expired') { /* 访问令牌肯定已过期,将当前请求传入获取刷新令牌方法, * 以便获取刷新令牌换取新的令牌后继续当前请求 */ GetRefreshToken(GetCurrentTime); } }

接下来则是通过传入旧的访问令牌和刷新令牌调用接口换取新的访问令牌,如下:

/// <summary> /// 刷新Token /// </summary> /// <returns></returns> [HttpPost("refresh-token")] public async Task<IActionResult> RefreshToken([FromBody] Request request) { //TODO 参数校验 var principal = GetPrincipalFromAccessToken(request.AccessToken); if (principal is null) { return Ok(false); } var id = principal.Claims.First(c => c.Type == JwtRegisteredClaimNames.Sub)?.Value; if (string.IsNullOrEmpty(id)) { return Ok(false); } var user = await context.Users.Include(d => d.UserRefreshTokens) .FirstOrDefaultAsync(d => d.Id == id); if (user is null || user.UserRefreshTokens?.Count() <= 0) { return Ok(false); } if (!user.IsValidRefreshToken(request.RefreshToken)) { return Ok(false); } user.RemoveRefreshToken(request.RefreshToken); var refreshToken = GenerateRefreshToken(); user.CreateRefreshToken(refreshToken, id); try { await context.SaveChangesAsync(); } catch (Exception ex) { throw ex; } var claims = new Claim[] { new Claim(ClaimTypes.Name, user.UserName), new Claim(JwtRegisteredClaimNames.Email, user.Email), new Claim(JwtRegisteredClaimNames.Sub, user.Id), }; return Ok(new Response() { AccessToken = GenerateAccessToken(claims), RefreshToken = refreshToken }); }

(编辑:南平站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读