using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using QYZH.InteractiveMagazine.Infrastructure.Auth; using QYZH.InteractiveMagazine.Models.Settings; using System.IdentityModel.Tokens.Jwt; namespace QYZH.InteractiveMagazine.Infrastructure.Middleware; /// /// JWT 自动刷新中间件 /// 功能: /// 每次请求都刷新 Redis 中 Token 的过期时间(保持会话活跃) /// 前端永远使用同一个 Token,无需处理 Token 刷新 /// public class JwtAutoRefreshMiddleware { private readonly RequestDelegate _next; private readonly IConfiguration _configuration; private const string AdminTokenKeyPrefix = "InteractiveMagazine:AdminAuth:Token"; private const string WeChatTokenKeyPrefix = "InteractiveMagazine:WeChatAuth:Token"; public JwtAutoRefreshMiddleware(RequestDelegate next, IConfiguration configuration) { _next = next; _configuration = configuration; } public async Task InvokeAsync(HttpContext context) { var authHeader = context.Request.Headers.Authorization.FirstOrDefault(); if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ")) { var token = authHeader.Substring("Bearer ".Length).Trim(); await TryRefreshRedisTokenExpiryAsync(context, token); } await _next(context); } private async Task TryRefreshRedisTokenExpiryAsync(HttpContext context, string token) { try { var tokenHandler = new JwtSecurityTokenHandler(); if (tokenHandler.ReadToken(token) is not JwtSecurityToken jwtToken) { return; } var expiryTime = jwtToken.ValidTo; var remainingTime = expiryTime - DateTime.Now; // JWT 已过期,不处理 if (remainingTime <= TimeSpan.Zero) { return; } var userId = jwtToken.Claims.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) { return; } var wxUserId = jwtToken.Claims.FirstOrDefault(c => c.Type == JwtHelper.WxUserIdClaimType)?.Value; if (string.IsNullOrEmpty(wxUserId)) { return; } var jwtSettings = _configuration.GetSection("JwtSettings").Get(); if (jwtSettings == null || jwtSettings.ExpiryMinutes <= 0) { return; } // 每次请求都刷新 Redis 中 Token 的过期时间 await RefreshRedisTokenExpiryAsync(wxUserId,userId, token, jwtSettings.ExpiryMinutes); } catch { // 忽略自动刷新异常,由后续认证中间件处理 } } /// /// 刷新 Redis 中已存在 Token 的过期时间(保持会话活跃) /// private async Task RefreshRedisTokenExpiryAsync(string wxUserId,string userId, string currentToken, int expiryMinutes) { // 检查 Admin Token var adminTokenKey = $"{AdminTokenKeyPrefix}:{userId}"; var adminToken = await RedisHelper.GetAsync(adminTokenKey); if (!string.IsNullOrEmpty(adminToken)) { await RedisHelper.SetAsync(adminTokenKey, currentToken, TimeSpan.FromMinutes(expiryMinutes)); return; } // 检查 WeChat Token var wechatTokenKey = $"{WeChatTokenKeyPrefix}:{wxUserId}{userId}"; var wechatToken = await RedisHelper.GetAsync(wechatTokenKey); if (!string.IsNullOrEmpty(wechatToken)) { await RedisHelper.SetAsync(wechatTokenKey, currentToken, TimeSpan.FromMinutes(expiryMinutes)); } } }