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

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

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

如题,本节我们进入JWT最后一节内容,JWT本质上就是从身份认证服务器获取访问令牌,继而对于用户后续可访问受保护资源,但是关键问题是:访问令牌的生命周期到底设置成多久呢?见过一些使用JWT的童鞋会将JWT过期时间设置成很长,有的几个小时,有的一天,有的甚至一个月,这么做当然存在问题,如果被恶意获得访问令牌,那么可在整个生命周期中使用访问令牌,也就是说存在冒充用户身份,此时身份认证服务器当然也就是始终信任该冒牌访问令牌,若要使得冒牌访问令牌无效,唯一的方案则是修改密钥,但是如果我们这么做了,则将使得已授予的访问令牌都将无效,所以更改密钥不是最佳方案,我们应该从源头尽量控制这个问题,而不是等到问题呈现再来想解决之道,刷新令牌闪亮登场。

RefreshToken

什么是刷新令牌呢?刷新访问令牌是用来从身份认证服务器交换获得新的访问令牌,有了刷新令牌可以在访问令牌过期后通过刷新令牌重新获取新的访问令牌而无需客户端通过凭据重新登录,如此一来,既保证了用户访问令牌过期后的良好体验,也保证了更高的系统安全性,同时,若通过刷新令牌获取新的访问令牌验证其无效可将受访者纳入黑名单限制其访问,那么访问令牌和刷新令牌的生命周期设置成多久合适呢?这取决于系统要求的安全性,一般来讲访问令牌的生命周期不会太长,比如5分钟,又比如获取微信的AccessToken的过期时间为2个小时。接下来我将用两张表来演示实现刷新令牌的整个过程,可能有更好的方案,欢迎在评论中提出,学习,学习。我们新建一个:5000的WebApi用于身份认证,再新建一个:5001的客户端,首先点击【模拟登录获取Toen】获取访问令牌和刷新令牌,然后点击【调用客户端获取当前时间】,如下:

接下来我们新建一张用户表(User)和用户刷新令牌表(UserRefreshToken),结构如下:

public class User { public string Id { get; set; } public string Email { get; set; } public string UserName { get; set; } private readonly List<UserRefreshToken> _userRefreshTokens = new List<UserRefreshToken>(); public IEnumerable<UserRefreshToken> UserRefreshTokens => _userRefreshTokens; /// <summary> /// 验证刷新token是否存在或过期 /// </summary> /// <param></param> /// <returns></returns> public bool IsValidRefreshToken(string refreshToken) { return _userRefreshTokens.Any(d => d.Token.Equals(refreshToken) && d.Active); } /// <summary> /// 创建刷新Token /// </summary> /// <param></param> /// <param></param> /// <param></param> public void CreateRefreshToken(string token, string userId, double minutes = 1) { _userRefreshTokens.Add(new UserRefreshToken() { Token = token, UserId = userId, Expires = DateTime.Now.AddMinutes(minutes) }); } /// <summary> /// 移除刷新token /// </summary> /// <param></param> public void RemoveRefreshToken(string refreshToken) { _userRefreshTokens.Remove(_userRefreshTokens.FirstOrDefault(t => t.Token == refreshToken)); }

public class UserRefreshToken { public string Id { get; private set; } = Guid.NewGuid().ToString(); public string Token { get; set; } public DateTime Expires { get; set; } public string UserId { get; set; } public bool Active => DateTime.Now <= Expires; }

如上可以看到对于刷新令牌的操作我们将其放在用户实体中,也就是使用EF Core中的Back Fields而不对外暴露。接下来我们将生成的访问令牌、刷新令牌、验证访问令牌、获取用户身份封装成对应方法如下:

/// <summary> /// 生成访问令牌 /// </summary> /// <param></param> /// <returns></returns> public string GenerateAccessToken(Claim[] claims) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signingKey)); var token = new JwtSecurityToken( issuer: "http://localhost:5000", audience: "http://localhost:5001", claims: claims, notBefore: DateTime.Now, expires: DateTime.Now.AddMinutes(1), signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256) ); return new JwtSecurityTokenHandler().WriteToken(token); } /// <summary> /// 生成刷新Token /// </summary> /// <returns></returns> public string GenerateRefreshToken() { var randomNumber = new byte[32]; using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(randomNumber); return Convert.ToBase64String(randomNumber); } } /// <summary> /// 从Token中获取用户身份 /// </summary> /// <param></param> /// <returns></returns> public ClaimsPrincipal GetPrincipalFromAccessToken(string token) { var handler = new JwtSecurityTokenHandler(); try { return handler.ValidateToken(token, new TokenValidationParameters { ValidateAudience = false, ValidateIssuer = false, ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signingKey)), ValidateLifetime = false }, out SecurityToken validatedToken); } catch (Exception) { return null; } }

当用户点击登录,访问身份认证服务器,登录成功后我们创建访问令牌和刷新令牌并返回,如下:

[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="调用客户端获取当前时间" />

(编辑:云计算网_宿迁站长网)

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

热点阅读