You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 15:01:11