.NET 8 Blazor Server权限授权时AuthorizationHandlerContext.User为空
问题分析
在你的Blazor Server(Server交互模式)项目中,使用自定义权限授权时出现PermissionAuthorizationHandler内context.User为空的问题,核心原因是:你同时配置了基于HttpContext的JWT Bearer认证,但实际用户身份是通过CustomAuthStateProvider从浏览器本地存储获取的,授权系统默认优先从HttpContext读取用户信息,而HttpContext中并无有效用户数据,导致context.User为空。此外服务注册顺序和冗余配置进一步加剧了该问题。
解决方案
1. 移除冗余的HttpContext认证配置
删除ConfigureServices中AddAuthentication和AddJwtBearer的全部代码——你的Blazor Server应用通过本地存储保存token,不需要依赖HttpContext层面的JWT认证(该配置仅适用于Web API场景):
// 移除这段冗余代码 services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { // 原JWT验证参数配置代码 });
2. 调整服务注册顺序
确保自定义AuthenticationStateProvider在授权服务之前注册,仅保留必要的认证核心服务:
public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSyncfusionBlazor(); services.AddRazorComponents() .AddInteractiveServerComponents() .AddInteractiveWebAssemblyComponents(); AddRootDirectory(services); // 保留核心认证服务 services.AddAuthenticationCore(); services.AddCascadingAuthenticationState(); services.AddOutputCache(); services.AddBlazoredLocalStorage(); services.AddHttpContextAccessor(); // 优先注册自定义身份状态提供器 services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>(); // 注册授权相关服务 services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>(); services.AddSingleton<IAuthorizationPolicyProvider, FlexibleAuthorizationPolicyProvider>(); // 显式添加授权服务(若之前未添加) services.AddAuthorization(); }
3. 验证FlexibleAuthorizationPolicyProvider实现
确保该策略提供器能正确将[Authorize(Permissions.X)]标记转换为PermissionAuthorizationRequirement,示例实现参考:
public class FlexibleAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider { private readonly IAuthorizationPolicyProvider _fallbackProvider; public FlexibleAuthorizationPolicyProvider(IAuthorizationPolicyProvider fallbackProvider) : base(fallbackProvider) { _fallbackProvider = fallbackProvider; } public override async Task<AuthorizationPolicy> GetPolicyAsync(string policyName) { // 尝试解析权限枚举 if (Enum.TryParse<Permissions>(policyName, out var requiredPermissions)) { var policyBuilder = new AuthorizationPolicyBuilder(); policyBuilder.AddRequirements(new PermissionAuthorizationRequirement(requiredPermissions)); return policyBuilder.Build(); } // fallback到默认策略逻辑 return await _fallbackProvider.GetPolicyAsync(policyName); } }
4. 确认根组件的认证状态传递
检查App.razor中是否正确使用CascadingAuthenticationState包裹路由,确保子组件能获取到用户身份:
<CascadingAuthenticationState> <Router AppAssembly="@typeof(App).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> <NotFound> <PageTitle>Not found</PageTitle> <LayoutView Layout="@typeof(MainLayout)"> <p role="alert">Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router> </CascadingAuthenticationState>
5. 调试自定义身份提供器
- 登录后检查浏览器本地存储是否存在
authToken键值对 - 在
CustomAuthStateProvider的GetClaimsIdentity方法中添加日志,确认token解析出的Claims包含CustomClaimTypes.Permissions类型的声明
关键说明
Blazor Server的授权系统默认依赖AuthenticationStateProvider提供的用户身份,而非HttpContext。当你同时配置HttpContext层面的JWT认证时,授权系统会优先尝试从HttpContext读取用户,而此时HttpContext中无有效用户数据,导致context.User为空。移除冗余的HttpContext认证配置后,授权系统会正确使用CustomAuthStateProvider提供的用户身份。
内容的提问来源于stack exchange,提问作者Ahmad Salim

