添加Claims变压器后,注入的IPrincipal Claims为空

时间:2019-05-23 17:29:29

标签: c# asp.net-core dependency-injection

我试图在ASP.NET Core中使用Windows身份验证使用基于声明的授权。我有一个Claims变压器,可以设置用户的Claims。我还将IPrincipal插入DbContext中,以便在调用SaveChanges()记录谁更新的实体时可以访问当前用户。

在Startup.cs中:

    services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>()?.HttpContext?.User);
    services.AddTransient<IClaimsTransformation, MyClaimsTransformer>();

在MyDbContext.cs中:

    protected readonly IPrincipal _principal ;

    public MyDbContext(DbContextOptions<MyDbContext> options, IPrincipal principal) : base(options)
    {
        _principal = principal;
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        // Do something with Claim value...
        await base.SaveChangesAsync(cancellationToken);
    }

我添加了Claims变形器以添加一些Claim信息:

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        // Add Claims...
        return principal;
    }
在用户中间件中调用

SaveChangesAsync来记录对应用程序的访问。在添加Claims Transformer之前,注入IPrincipal的{​​{1}}是DbContext,它包含我需要的Claims,例如PrimarySid等。现在添加转换之后,它是一个WindowsPrincipal,但“索赔”列表现在为空。

通过调试,我可以验证ClaimsPrincipalTransformAsync之前执行,但是db上下文中的构造函数注入发生在转换之前(SaveChangesAsync是作用域服务,Claims Transfomer是瞬态)。

摘要:

  • 在添加Claims变压器之前,注入的DbContext是带有声明的列表的IPrincipal
  • 添加了Claims变压器之后,注入的WindowsPrincipalIPrincipal,其中的Claims列表为空。

1 个答案:

答案 0 :(得分:0)

当尝试通过DbContext构造函数注入IPrincipal时,我遇到了完全相同的问题。我发现在DbContext中注入的IPrincipal尚未由ClaimsTransformer进行“转换”。

我通过创建UserResolverService来解决此问题。

IUserResolverService.cs

public interface IUserResolverService
{
    IPrincipal GetUser();
}

UserResolverService.cs

public class UserResolverService : IUserResolverService
{
    private readonly IHttpContextAccessor _context;

    public UserResolverService(IHttpContextAccessor context)
    {
        _context = context;
    }

    public IPrincipal GetUser()
    {
        return _context.HttpContext.User;
    }
}

MyDbContext.cs

private readonly IUserResolverService _userResolverService;

// Call this whenever you need the IPrincipal object
public IPrincipal User => _userResolverService.GetUser();

public MyDbContext(IUserResolverService userResolverService, DbContextOptions<MyDbContext> options)
        : base(options)
{
    _userResolverService = userResolverService;
}

Startup.cs

services.AddTransient<IClaimsTransformation, ClaimsTransformer>();
services.AddTransient<IUserResolverService, UserResolverService>();