访问令牌(AccessToken)与刷新令牌(RefreshToken)

源码点我

闲着无聊的我又来了,今天介绍一下访问令牌(AccessToken)与刷新令牌(RefreshToken)。

最近公司来了几个Java,然后空气顿时充满了学术的氛围,每天都弥漫着垃圾回收,内存分配,堆栈等等各种技术问题的讨论,竖起了我的小耳朵,我偶然间听到了这个Token过期的问题,今天来说一说。

众所周知,JWT是我们常用的Token类型之一,但是JWT是无状态的,所有的信息,包括超时时间都会编码在JWT中。如果Token设置的超时时间过长会显得有些不安全,如果设置的过短则必须频繁的登录。

在IdentityServer4中,我们可以通过设置AllowOfflineAccess = true为Client设置一个刷新令牌,该令牌可以在Token失效返回401的时候去访问刷新接口,从而获得一个新的访问令牌和一个刷新令牌,但是我们的项目中好像没有用IdentityServer4啊,往下看。

1、打开我们之前的NET6.API项目,我们在LoginView中增加一个字段RefreshToken,并且在登录的时候生成一个Guid(你可以随意定义)作为RefreshToken返回给客户端,通过cache或者redis将该RefreshToken缓存起来,设置一个较长的缓存时间,例如30天。
在这里插入图片描述
在这里插入图片描述
2、调用登录接口,发现除了AccessToken,RefreshToken也返回了。
在这里插入图片描述
3、接着我们新增一个接口,叫做刷新访问令牌。

 /// <summary>
        /// 刷新访问令牌
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost("refresh")]
        [ProducesResponseType(typeof(LoginView), StatusCodes.Status200OK)]
        public async Task<IActionResult> RefreshAsync(RefreshTokenDto dto)
        {
            try
            {
                var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(dto.AccessToken);
                var userid = jwtToken.Claims.FirstOrDefault(a => a.Type == ClaimTypes.NameIdentifier)?.Value;
                var username = jwtToken.Claims.FirstOrDefault(a => a.Type == ClaimTypes.Name)?.Value;
                var refreshtoken = CacheHelper.Get<string>($"{CacheEnum.刷新令牌}_{userid}");
                if (refreshtoken == null) return Ok(JsonView("未找到该刷新令牌"));
                if (refreshtoken != dto.RefreshToken) return Ok(JsonView("刷新令牌不正确"));
                #region 签发JWT
                //生成一个新的刷新令牌
                refreshtoken = CommonFun.GUID;
                CacheHelper.Set($"{CacheEnum.刷新令牌}_{userid}", refreshtoken, TimeSpan.FromDays(30));
                var view = new LoginView
                {
                    Expires = DateTime.Now.AddDays(7),
                    RefreshToken = refreshtoken
                };
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["JwtSecurityKey"]));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                var token = new JwtSecurityToken(
                    issuer: "net6api.com",
                    audience: "net6api.com",
                    claims: jwtToken.Claims,
                    expires: view.Expires,
                    signingCredentials: creds);
                view.AccessToken = new JwtSecurityTokenHandler().WriteToken(token);
                return Ok(JsonView(view));
                #endregion
            }
            catch (Exception)
            {
                return Ok(JsonView("访问令牌解析失败"));
            }
        }

4、我们调用刷新访问令牌接口,传入旧的AccessToken和RefreshToken,发现接口已经可以成功返回了一组新的Token。
在这里插入图片描述
【注意】重点来了!重点来了!重点来了!因为无论是Token是失效还是过期,API返回的也都是401未授权状态,那么前端如何能知道当前的AccessToken是失效还是过期呢,我们可以通过以下手段来实现
打开Program,在AddAuthentication中新增一个JwtBearerEvents,用来监听Toke过期事件,并且往Header中添加一个自定义keyValu,然后我们调整Token的失效时间为5秒,再次尝试。

在这里插入图片描述
在这里插入图片描述
至此,前端可以通过Response中是否存在自定义Header来判断当前AccessToken是否过期。

前端逻辑如下:
调用login接口登录获取AccessToken和RefreshToken,然后各种调接口…
突然发现HTTPStateCode返回401,此时判断是否存在自定义Header,如果存在则调用refresh刷新Token,继续各种调接口…
如果不存在自定义Header,则重新调用login接口进行登录。
结束,继续摸鱼…

  • 10
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值