闽公网安备 35020302035485号
后来笔者的做法是:当客户端每次发起Http请求时,先判断本地Token是否存在:
1. 如果不存在,则先向服务端发起登录验证请求,从而获取Token。
2. 如果已存在,则检测Token是否即将过期。如果是的话,就重新发起登录验证更新Token,否则继续使用当前Token。其中判断Token是否即将过期没有一个标准设定,个人认为在1~5分钟之间比较合适。 以上就是实现Token自动续期的整个过程。

public record TokenResult
{
/// <summary>
/// 访问令牌
/// </summary>
public string AccessToken { get; init; }
/// <summary>
/// 过期时间
/// </summary>
public DateTime ExpiredTime { get; init; }
}
服务端实现public class Program
{
public static void Main(string[] args)
{
// 堆代码 duidaima.com
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "Name",
RoleClaimType = "Role",
ValidateAudience = false,
ValidateIssuer = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromSeconds(30),
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtConsts.SigningKey))
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
DemoController 控制器:提供 LoginAsync() 和 GetCurrentTimeAsync() 两个方法,代码如下:[ApiController]
[Route("[controller]")]
public class DemoController : ControllerBase
{
/// <summary>
/// 登录
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("Login")]
public async ValueTask<TokenResult> LoginAsync(LoginDto dto)
{
var user = GetUserInfo(dto.UserName);
if (user.Password == dto.Password) // 登录密码验证
{
TokenResult tokenResult = await JwtHelper.GenerateAsync(user.Id, user.UserName, user.Name, user.PhoneNumber);
return tokenResult;
}
return null;
}
/// <summary>
/// 获取当前时间
/// </summary>
/// <returns></returns>
[Authorize]
[HttpGet("CurrentTime")]
public ValueTask<DateTimeOffset> GetCurrentTimeAsync()
{
return ValueTask.FromResult(DateTimeOffset.Now);
}
}
第26行代码:给 GetCurrentTimeAsync() 加上 [Authorize] 特性后, 当前服务必须授权后才能访问。public static class JwtHelper
{
/// <summary>
/// 生成Token
/// </summary>
/// <returns></returns>
public static ValueTask<TokenResult> GenerateAsync(int id, string username, string name, string phoneNumber)
{
var claims = new List<Claim>()
{
new Claim("UserId", id.ToString()), // 用户Id
new Claim("UserName", username), // 用户名
new Claim("Name", name) , // 姓名
new Claim("PhoneNumber", phoneNumber) // 手机号码
};
var tokenHandler = new JwtSecurityTokenHandler();
var expiresAt = DateTime.Now.AddMinutes(20); // 过期时间
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = expiresAt,
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(JwtConsts.SigningKey)),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return ValueTask.FromResult(new TokenResult
{
AccessToken = tokenString,
ExpiredTime = expiresAt
});
}
}
第18行代码:设置Token的过期时间,这里我们把有效期设为20分钟。[Headers(new[] { "Authorization:Bearer" })]
public interface IDemoApi
{
/// <summary>
/// 获取当前时间
/// </summary>
/// <returns></returns>
[Get("/Demo/CurrentTime")]
Task<DateTimeOffset> GetCurrentTimeAsync();
}
第1行代码:给 IDemApi 接口加上 [Headers(...)] 特性,这样每次调用 GetCurrentTimeAsync() 方法,Http请求头部都会携带此信息。JWT的标准头部格式为:Authorization: Bearer <token>。接下来,就是实现Token自动续期功能。笔者封装了一个 RestHelper 类,核心代码如下:/// <summary>
/// Rest请求服务
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T For<T>()
{
var settings = new RefitSettings()
{
AuthorizationHeaderValueGetter = () => GetTokenAsync(),
};
return RestService.For<T>(BaseUrl, settings);
}
/// <summary>
/// 获取Token
/// </summary>
/// <returns></returns>
private static async Task<string> GetTokenAsync()
{
if (TokenResult is null || DateTimeOffset.Now.AddMinutes(1) >= TokenResult?.ExpiredTime)
{
var uri = new Uri($"{BaseUrl}/demo/login", UriKind.Absolute);
var dto = new LoginDto { UserName = "fjq", Password = "123456" };
using var httpResMsg = await new HttpClient().PostAsync(uri, JsonContent.Create(dto));
if (httpResMsg.IsSuccessStatusCode)
{
var jsonStr = await httpResMsg.Content.ReadAsStringAsync();
TokenResult = JsonHelper.FromJson<TokenResult>(jsonStr);
}
}
return TokenResult?.AccessToken;
}
第10行代码:AuthorizationHeaderValueGetter 是 RefitSettings 对象的一个委托属性,用来提供授权头部信息,即JWT字符串。var dt = await RestHelper.For<IDemoApi>().GetCurrentTimeAsync();
界面运行效果如下(亲测有效):