Files
QYZH.InteractiveMagazine/QYZH.InteractiveMagazine.Infrastructure/Middleware/JwtAutoRefreshMiddleware.cs
2026-06-29 17:49:28 +08:00

107 lines
3.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/// <summary>
/// JWT 自动刷新中间件
/// 功能:
/// 每次请求都刷新 Redis 中 Token 的过期时间(保持会话活跃)
/// 前端永远使用同一个 Token无需处理 Token 刷新
/// </summary>
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<JwtSettings>();
if (jwtSettings == null || jwtSettings.ExpiryMinutes <= 0)
{
return;
}
// 每次请求都刷新 Redis 中 Token 的过期时间
await RefreshRedisTokenExpiryAsync(wxUserId,userId, token, jwtSettings.ExpiryMinutes);
}
catch
{
// 忽略自动刷新异常,由后续认证中间件处理
}
}
/// <summary>
/// 刷新 Redis 中已存在 Token 的过期时间(保持会话活跃)
/// </summary>
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));
}
}
}