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

ASP.NET Core 7添加Identity遇服务构造错误,用户上下文无Claims

解决ASP.NET Core 7自定义AuthorizationHandler无法获取用户Claims及Identity集成报错问题

问题分析

  1. 自定义AuthorizationHandler触发后context.User无Claims、无法使用IsInRole():核心原因是认证中间件顺序错误,导致授权流程无法读取已认证用户的信息(你手动生成Claims的逻辑本身是正确的)。
  2. 添加AddIdentity<IdentityUser, IdentityRole>报错:ASP.NET Core Identity默认依赖IUserStore<TUser>实现(比如EF Core的UserStore),你未配置对应的用户存储实现,导致依赖注入容器无法解析必要服务。

解决方案一:无需集成Identity,修复手动认证流程

你已经通过手动生成ClaimsPrincipal完成登录,完全不需要依赖ASP.NET Core Identity,只需调整中间件顺序即可解决问题:

1. 调整Startup.cs的中间件顺序

在Configure方法中,必须保证UseAuthentication()在UseAuthorization()之前:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ... 其他中间件(如UseStaticFiles、UseRouting等)

    // 先启用认证,让系统识别已登录用户
    app.UseAuthentication();
    // 再启用授权,此时授权流程能读取到用户Claims
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
    });
}

2. 移除不必要的Identity注册

删除services.AddIdentity<IdentityUser, IdentityRole>();,因为你不需要Identity的用户管理功能,手动认证逻辑已足够。

3. 确认登录代码有效性

你的登录代码已正确添加Role Claim,可额外确认ClaimsIdentity的认证状态(指定认证方案后会自动标记为已认证):

// Login Action
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userObj.UserId.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Name, userObj.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, userObj.Role));
var principal = new ClaimsPrincipal(identity);

await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);

解决方案二:若必须集成IdentityCore(自定义用户存储)

如果确实需要使用Identity的部分功能(如ClaimsPrincipalFactory),可使用AddIdentityCore并手动实现极简版IUserStore<TUser>:

1. 注册IdentityCore并配置自定义存储

在ConfigureServices中替换原有的Identity注册逻辑:

// 注册IdentityCore,仅保留核心功能
services.AddIdentityCore<IdentityUser>(options =>
{
    // 按需配置Identity选项(如密码规则,不需要可留空)
})
.AddRoles<IdentityRole>() // 若需要角色支持则添加
.AddUserStore<CustomUserStore>(); // 绑定自定义用户存储

// 保留原有认证、授权配置
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options => {
        options.LoginPath = new PathString("/login");
        options.AccessDeniedPath = new PathString("/denied");
    });

services.AddSingleton<IAuthorizationHandler, RoleHandler>();
services.AddAuthorization(options =>
{
    options.AddPolicy("Roles", policy =>
    {
        policy.Requirements.Add(new RoleAuthRequirement { Role = "Role1" });
        policy.Requirements.Add(new RoleAuthRequirement { Role = "Role2" });
        policy.Requirements.Add(new RoleAuthRequirement { Role = "Role3" });
    });
});

2. 实现极简版IUserStore

针对模拟数据库场景,仅实现必要方法即可(未用到的方法可抛出异常):

public class CustomUserStore : IUserStore<IdentityUser>
{
    // 模拟内存数据库
    private readonly Dictionary<string, IdentityUser> _users = new();

    public Task<IdentityResult> CreateAsync(IdentityUser user, CancellationToken cancellationToken)
    {
        _users[user.Id] = user;
        return Task.FromResult(IdentityResult.Success);
    }

    public Task<IdentityResult> DeleteAsync(IdentityUser user, CancellationToken cancellationToken)
    {
        _users.Remove(user.Id);
        return Task.FromResult(IdentityResult.Success);
    }

    public Task<IdentityUser> FindByIdAsync(string userId, CancellationToken cancellationToken)
    {
        _users.TryGetValue(userId, out var user);
        return Task.FromResult(user);
    }

    public Task<IdentityUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
    {
        var user = _users.Values.FirstOrDefault(u => u.NormalizedUserName == normalizedUserName);
        return Task.FromResult(user);
    }

    public Task<string> GetNormalizedUserNameAsync(IdentityUser user, CancellationToken cancellationToken)
    {
        return Task.FromResult(user.NormalizedUserName);
    }

    public Task<string> GetUserIdAsync(IdentityUser user, CancellationToken cancellationToken)
    {
        return Task.FromResult(user.Id);
    }

    public Task<string> GetUserNameAsync(IdentityUser user, CancellationToken cancellationToken)
    {
        return Task.FromResult(user.UserName);
    }

    public Task SetNormalizedUserNameAsync(IdentityUser user, string normalizedName, CancellationToken cancellationToken)
    {
        user.NormalizedUserName = normalizedName;
        return Task.CompletedTask;
    }

    public Task SetUserNameAsync(IdentityUser user, string userName, CancellationToken cancellationToken)
    {
        user.UserName = userName;
        return Task.CompletedTask;
    }

    public Task<IdentityResult> UpdateAsync(IdentityUser user, CancellationToken cancellationToken)
    {
        _users[user.Id] = user;
        return Task.FromResult(IdentityResult.Success);
    }

    // 未用到的接口方法可直接抛出异常
    public void Dispose() { }
    public Task<IdentityUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }
    // 其他未实现方法同理
}

验证效果

调整完成后,RoleHandler中的context.User应该能正常读取到Claims,IsInRole()方法也能正常判断用户角色。若仍有问题,可在Handler中打印context.User.Claims排查具体Claim内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 04:40:56