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

ABP 6.0.3关闭浏览器后HangFire Dashboard出现401错误求助

问题原因
  1. Token存储与传递差异:ABP Angular默认将token存在localStorage中,前端路由请求会自动携带Authorization头,但访问HangFire Dashboard这类后端直接暴露的页面时,浏览器发起的是独立GET请求,不会自动从localStorage读取并携带token,导致HangFire验证无有效凭证。
  2. 登录态同步问题:关闭浏览器重进ABP应用时,前端通过localStorage的刷新令牌自动获取新的access token,但这个token未同步到HangFire验证依赖的存储(比如Cookie);重新登录时ABP会将token写入Cookie,此时HangFire能获取到有效凭证,验证通过。
  3. HangFire验证未适配ABP续期逻辑:ABP前端会自动续期access token,但HangFire的验证中间件未感知新token,旧token过期后就返回401。
解决方法

方法一:URL传递token并验证

后端配置

自定义HangFire授权过滤器,支持从URL参数读取token并通过ABP策略验证:

public class AbpHangFireAuthorizationFilter : IDashboardAuthorizationFilter
{
    private readonly string _policyName;
    private readonly IAuthorizationService _authorizationService;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AbpHangFireAuthorizationFilter(string policyName, IAuthorizationService authorizationService, IHttpContextAccessor httpContextAccessor)
    {
        _policyName = policyName;
        _authorizationService = authorizationService;
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<bool> AuthorizeAsync(DashboardContext context)
    {
        var httpContext = _httpContextAccessor.HttpContext;
        // 从URL参数获取token并写入请求头
        var token = context.Request.Query["access_token"].FirstOrDefault();
        if (!string.IsNullOrEmpty(token))
        {
            httpContext.Request.Headers.Append("Authorization", $"Bearer {token}");
        }
        // 验证ABP授权策略
        var result = await _authorizationService.AuthorizeAsync(httpContext.User, null, _policyName);
        return result.Succeeded;
    }
}

在Program.cs中配置HangFire Dashboard使用该过滤器:

app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[]
    {
        new AbpHangFireAuthorizationFilter("AbpIdentity.Admin", app.Services.GetRequiredService<IAuthorizationService>(), app.Services.GetRequiredService<IHttpContextAccessor>())
    },
    IgnoreAntiforgeryToken = true
});

前端配置

跳转HangFire时从ABP AuthService获取当前有效token,作为URL参数传递:

import { AuthService } from '@abp/ng.core';

async navigateToHangFire() {
    const token = await this.authService.getAccessToken();
    window.open(`/hangfire?access_token=${token}`, '_blank');
}

方法二:将token同步到Cookie

修改Angular配置,让ABP将token同时写入Cookie,访问HangFire时浏览器自动携带:

前端配置

在environment.ts的oAuthConfig中添加Cookie存储配置:

export const environment = {
  // ...其他配置
  oAuthConfig: {
    issuer: 'https://your-domain.com',
    redirectUri: window.location.origin,
    clientId: 'YourAngularClientId',
    responseType: 'code',
    scope: 'offline_access YourApiScope',
    storeAuthStateInCookie: true // 开启Cookie存储token
  }
};

后端配置

确保HangFire使用Cookie验证策略:

app.UseAuthentication();
app.UseAuthorization();

app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[]
    {
        new AuthorizationPolicyFilter(new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build())
    }
});

方法三:让HangFire复用ABP Bearer验证

将HangFire中间件放在ABP验证中间件之后,确保请求经过Bearer验证:

app.UseAuthentication();
app.UseAuthorization();
app.UseOpenIddictValidation(); // 启用OpenIddict验证

app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[]
    {
        new AuthorizationPolicyFilter(new AuthorizationPolicyBuilder()
            .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
            .RequireAuthenticatedUser()
            .Build())
    }
});

内容的提问来源于stack exchange,提问作者Jerry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 04:33:10