ASP.NET MVC RazorPages如何自动重定向刷新令牌后返回原端点?
实现RazorPages中JWT过期自动刷新并重定向回原端点的方案
1. 自定义授权过滤器(拦截请求)
创建继承IAuthorizationFilter的过滤器,在请求授权阶段检查JWT是否过期,过期则保存原请求地址并跳转到刷新令牌页面。
public class JwtRefreshAuthorizationFilter : IAuthorizationFilter { private readonly IHttpContextAccessor _httpContextAccessor; public JwtRefreshAuthorizationFilter(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public void OnAuthorization(AuthorizationFilterContext context) { var httpContext = _httpContextAccessor.HttpContext; var jwtToken = httpContext.Request.Cookies["JwtToken"]; // 假设JWT存储在Cookie中 if (string.IsNullOrEmpty(jwtToken)) { context.Result = new RedirectToPageResult("/Account/Login"); return; } var tokenHandler = new JwtSecurityTokenHandler(); try { var validationParams = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("你的密钥字符串")), ValidateIssuer = false, ValidateAudience = false, ClockSkew = TimeSpan.Zero }; tokenHandler.ValidateToken(jwtToken, validationParams, out var validatedToken); var jwt = validatedToken as JwtSecurityToken; var expiresAt = jwt?.ValidTo ?? DateTime.UtcNow; if (expiresAt <= DateTime.UtcNow) { // 保存原请求的完整路径(含查询参数) httpContext.Session.SetString("OriginalRequestUrl", httpContext.Request.Path + httpContext.Request.QueryString); context.Result = new RedirectToPageResult("/Account/RefreshToken"); } } catch (SecurityTokenExpiredException) { httpContext.Session.SetString("OriginalRequestUrl", httpContext.Request.Path + httpContext.Request.QueryString); context.Result = new RedirectToPageResult("/Account/RefreshToken"); } catch (Exception) { context.Result = new RedirectToPageResult("/Account/Login"); } } }
2. 注册过滤器与Session支持
在Program.cs中注册过滤器和Session服务,确保Session中间件顺序正确。
builder.Services.AddScoped<JwtRefreshAuthorizationFilter>(); builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(10); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); builder.Services.AddRazorPages(options => { // 全局应用过滤器,或在指定PageModel上用[TypeFilter(typeof(JwtRefreshAuthorizationFilter))]标记 options.Filters.AddService<JwtRefreshAuthorizationFilter>(); }); // 中间件顺序:Session需在UseRouting之前 app.UseSession(); app.UseRouting();
3. 实现刷新令牌的RazorPage
创建/Pages/Account/RefreshToken.cshtml.cs,处理令牌刷新逻辑,完成后重定向回原请求地址。
public class RefreshTokenModel : PageModel { private readonly HttpClient _httpClient; public RefreshTokenModel(HttpClient httpClient) { _httpClient = httpClient; } public async Task<IActionResult> OnGetAsync() { var refreshToken = HttpContext.Request.Cookies["RefreshToken"]; if (string.IsNullOrEmpty(refreshToken)) { return RedirectToPage("/Account/Login"); } // 调用API的刷新令牌端点 var response = await _httpClient.PostAsJsonAsync("api/auth/refresh", new { RefreshToken = refreshToken }); if (!response.IsSuccessStatusCode) { return RedirectToPage("/Account/Login"); } var tokenResult = await response.Content.ReadFromJsonAsync<TokenResponse>(); // 更新Cookie中的JWT和新RefreshToken Response.Cookies.Append("JwtToken", tokenResult.JwtToken, new CookieOptions { HttpOnly = true, Secure = true, SameSite = SameSiteMode.Strict, Expires = DateTime.UtcNow.AddMinutes(30) }); Response.Cookies.Append("RefreshToken", tokenResult.RefreshToken, new CookieOptions { HttpOnly = true, Secure = true, SameSite = SameSiteMode.Strict, Expires = DateTime.UtcNow.AddDays(7) }); // 获取原请求地址,默认跳首页 var originalUrl = HttpContext.Session.GetString("OriginalRequestUrl") ?? "/"; HttpContext.Session.Remove("OriginalRequestUrl"); return Redirect(originalUrl); } } // 自定义响应模型,与API返回结构匹配 public class TokenResponse { public string JwtToken { get; set; } public string RefreshToken { get; set; } }
关键注意事项
- Cookie安全配置:JWT和RefreshToken的Cookie必须设置
HttpOnly、Secure、SameSite=Strict,防范XSS和CSRF攻击。 - 过滤器范围控制:若无需全局拦截,可在需要授权的PageModel上单独添加
[TypeFilter(typeof(JwtRefreshAuthorizationFilter))]特性。 - 异常兜底:刷新令牌失败时直接跳转登录页,避免用户陷入循环。
内容的提问来源于stack exchange,提问作者dEISOT
相关产品推荐
相关产品推荐

