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

[HttpPost("login")] public async Task<IActionResult> Login() { var user = new User() { Id = "D21D099B-B49B-4604-A247-71B0518A0B1C", UserName = "Jeffcky", Email = "2752154844@qq.com" }; await context.Users.AddAsync(user); var refreshToken = GenerateRefreshToken(); user.CreateRefreshToken(refreshToken, user.Id); await context.SaveChangesAsync(); 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 }); }

此时我们回到如上给出的图,我们点击【模拟登录获取Token】,此时发出Ajax请求,然后将返回的访问令牌和刷新令牌存储到本地localStorage中,如下:

<input type="button" value="模拟登录获取Token" /> <input type="button" value="调用客户端获取当前时间" />

//模拟登陆 $('#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请求获取刷新令牌,如下:

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

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