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

//获取刷新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 }); }

如上通过传入旧的访问令牌验证并获取用户身份,然后验证刷新令牌是否已经过期,如果未过期则创建新的访问令牌,同时更新刷新令牌。最终客户端访问令牌过期的那一刻,通过刷新令牌获取新的访问令牌继续调用上一请求,如下:

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

到这里关于JWT实现刷新Token就已结束,自我感觉此种实现刷新令牌将其存储到数据库的方案还算可取,将刷新令牌存储到Redis也可行,看个人选择吧。上述若刷新令牌验证无效,可将访问者添加至黑名单,不过是添加一个属性罢了。别着急,本节内容结束前,还留有彩蛋。

EntityFramework Core Back Fields深入探讨

无论是看视频还是看技术博客也好,一定要动手验证,看到这里觉得上述我所演示是不是毫无问题,如果阅读本文的你直接拷贝上述代码你会发现有问题,且听我娓娓道来,让我们来复习下Back Fields。Back Fields命名是有约定dei,上述我是根据约定而命名,所以千万别一意孤行,别乱来,比如如下命名将抛出如下异常:

private readonly List<UserRefreshToken> _refreshTokens = new List<UserRefreshToken>(); public IEnumerable<UserRefreshToken> UserRefreshTokens => _refreshTokens;

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

上述我们配置刷新令牌的Back Fields,代码如下:

private readonly List<UserRefreshToken> _userRefreshTokens = new List<UserRefreshToken>(); public IEnumerable<UserRefreshToken> UserRefreshTokens => _userRefreshTokens;

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

要是我们配置成如下形式,结果又会怎样呢?

private readonly List<UserRefreshToken> _userRefreshTokens = new List<UserRefreshToken>(); public IEnumerable<UserRefreshToken> UserRefreshTokens => _userRefreshTokens.AsReadOnly();

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

此时为了解决这个问题,我们必须将其显式配置成Back Fields,如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/5d8768a460568b52d81c999d92982fd5.html