详解ASP.NET Core Web Api之JWT刷新Token
//模仿登岸 $('#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 }); } (编辑:湖南网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |