ASP.NET Core 2.0.3 ClaimsTransformer в сочетании с HttpContextAccessor, утверждения очищаются

В моем приложении Asp.Net Core я хочу добавить пользовательские утверждения в свой ClaimsIdentity, чтобы иметь доступ к ним на разных уровнях моего приложения. Для этого я добавил следующий код

Запуск

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddTransient<IPrincipal>(
            provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);
        services.AddTransient<IClaimsTransformation, ClaimsTransformer>();

Преобразователь претензий

public class ClaimsTransformer : IClaimsTransformation
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly IPrincipal _principal;
    public ClaimsTransformer(IUnitOfWork unitOfWork, IPrincipal principal)
    {
        _unitOfWork = unitOfWork;
        _principal = principal;
    }
    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        var currentPrincipal  = (ClaimsIdentity)_principal.Identity;
        var identity = (ClaimsIdentity)principal.Identity;
        if (currentPrincipal.Claims.All(p => p.Type != "UserId"))
        {
            var person = _unitOfWork.PersonRepository.GetPersonBySubjectId(principal.Claims.First(p => p.Type == "sub").Value);
            person.Wait();
            if (person.Result != null)
            {
                currentPrincipal.AddClaim(new Claim("UserId", person.Result.Id.ToString()));
                currentPrincipal.AddClaim(new Claim("TenantId", person.Result.PersonTeams.FirstOrDefault(p => p.Team.TeamType == TeamType.OrganizationTeam)?.Team.OrganizationId.ToString()));
                if (principal.Claims.Any(p => p.Type == "Admin"))
                {
                    currentPrincipal.AddClaim(new Claim("Admin", "True"));
                }
            }
            foreach (var claim in identity.Claims)
            {
                currentPrincipal.AddClaim(claim);
            }
        }
        return Task.FromResult(principal);
    }
}

Чего я не понимаю, так это того, что когда я запускаю преобразование Claims и выполняю код, все необходимые утверждения доступны, но когда я внедряю свой IPrincipal в пользовательский класс, коллекция утверждений пуста, когда я не использую ClaimsTransformation, утверждения доступны через введенный IPrincipal.

Чтобы решить эту проблему, я добавляю свой IPrincipal в ClaimsTransformer, дублирую утверждения из входного параметра TransformAsync и добавляю UserId и TenantId. Это работает, но проблема в том, что я не понимаю, почему претензии удаляются, когда я запускаю ClaimsTransformer, и почему мне нужно добавить этот хак




Ответы (1)


Я был на том же месте. Мне пришлось удалить IPrincipal DI

Запуск

services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IClaimsTransformation, ClaimsTransformer>();

services.AddAuthentication(IISDefaults.AuthenticationScheme);

services.AddAuthorization(options =>
        {
            options.AddPolicy("SystemAdminOnly", policy => policy.RequireClaim(ClaimTypes.Role, "SystemAdmin"));
        });

Преобразователь претензий

public ClaimsTransformer(IRepository repository, IHttpContextAccessor httpContextAccessor/*, IPrincipal principal*/, IMemoryCache cache)
    {
        _repository = repository;
        _httpContextAccessor = httpContextAccessor;
       // _principal = principal;
        _cache = cache;
    }

public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
   {
        if (principal.Identity.IsAuthenticated)
        {
            var currentPrincipal = (ClaimsIdentity)principal.Identity;//_principal.Identity;

            var ci = (ClaimsIdentity)principal.Identity;
            var cacheKey = ci.Name;

            if (_cache.TryGetValue(cacheKey, out List<Claim> claims))
            {
                currentPrincipal.AddClaims(claims);
            }
            else
            {
                claims = new List<Claim>();
                var isUserSystemAdmin = await _repository.IsUserAdmin(ci.Name);
                if (isUserSystemAdmin)
                {
                    var c = new Claim(ClaimTypes.Role, "SystemAdmin");
                    claims.Add(c);
                }

                _cache.Set(cacheKey, claims);
                currentPrincipal.AddClaims(claims);
            }

            //foreach (var claim in ci.Claims)
            //{
            //    currentPrincipal.AddClaim(claim);
            //}
        }

        return await Task.FromResult(principal);
    }

И это работает!

person Steve Tolba    schedule 04.12.2017