Заполнение настраиваемого утверждения из SQL с помощью приложения с проверкой подлинности Windows в .Net Core

Сценарий - Интранет-приложение .Net Core в Active Directory с использованием SQL Server для управления разрешениями приложений и расширенной идентификацией пользователей.

Успех на сегодняшний день - пользователь аутентифицирован, и доступны утверждения Windows (имя и группы). Identity.Name можно использовать для возврата модели пользователя домена из базы данных с расширенными свойствами.

Проблема и вопрос. Затем я пытаюсь заполнить одно настраиваемое свойство утверждения «Id» и сделать его глобально доступным через ClaimsPrincipal. Я изучил ClaimsTransformation до сих пор без особого успеха. В других статьях я читал, что вы ДОЛЖНЫ добавлять претензии перед входом в систему, но может ли это быть правдой? Это означало бы полную зависимость от AD для выполнения всех требований, так ли это на самом деле?

Ниже мой простой код на этом этапе в HomeController. Я попадаю в базу данных, а затем пытаюсь заполнить ClaimsPrincipal, но затем возвращаю модель пользователя домена. Я думаю, что это может быть моя проблема, но я новичок в авторизации в .net и изо всех сил пытаюсь разобраться в претензиях.

Большое спасибо за всю полученную помощь

Текущий код:

public IActionResult Index()
        {
            var user = GetExtendedUserDetails();
            User.Claims.ToList();
            return View(user);
        }

        private Models.User GetExtendedUserDetails()
        {
            var user = _context.User.SingleOrDefault(m => m.Username == User.Identity.Name.Remove(0, 6));
            var claims = new List<Claim>();

            claims.Add(new Claim("Id", Convert.ToString(user.Id), ClaimValueTypes.String));
            var userIdentity = new ClaimsIdentity("Intranet");
            userIdentity.AddClaims(claims);
            var userPrincipal = new ClaimsPrincipal(userIdentity);

            return user;
        }

ОБНОВЛЕНИЕ:

Я зарегистрировал ClaimsTransformation

app.UseClaimsTransformation(o => new ClaimsTransformer().TransformAsync(o));

и построил ClaimsTransformer, как показано ниже, в соответствии с этим запросом github

https://github.com/aspnet/Security/issues/863

public class ClaimsTransformer : IClaimsTransformer
{
    private readonly TimesheetContext _context;
    public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
    {

        System.Security.Principal.WindowsIdentity windowsIdentity = null;

        foreach (var i in context.Principal.Identities)
        {
            //windows token
            if (i.GetType() == typeof(System.Security.Principal.WindowsIdentity))
            {
                windowsIdentity = (System.Security.Principal.WindowsIdentity)i;
            }
        }

        if (windowsIdentity != null)
        {
            //find user in database
            var username = windowsIdentity.Name.Remove(0, 6);
            var appUser = _context.User.FirstOrDefaultAsync(m => m.Username == username);

            if (appUser != null)
            {

                ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("Id", Convert.ToString(appUser.Id)));

                /*//add all claims from security profile
                foreach (var p in appUser.Id)
                {
                    ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim(p.Permission, "true"));
                }*/

            }

        }
        return await System.Threading.Tasks.Task.FromResult(context.Principal);
    }
}

Но я получаю NullReferenceException: ссылка на объект не установлена ​​на экземпляр ошибки объекта, несмотря на то, что ранее вернула модель предметной области.

С STARTUP.CS

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Birch.Intranet.Models;
using Microsoft.EntityFrameworkCore;

namespace Birch.Intranet
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthorization();

            // Add framework services.
            services.AddMvc();
            // Add database
            var connection = @"Data Source=****;Initial Catalog=Timesheet;Integrated Security=True;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
            services.AddDbContext<TimesheetContext>(options => options.UseSqlServer(connection));

            // Add session
            services.AddSession(options => {
                options.IdleTimeout = TimeSpan.FromMinutes(60);
                options.CookieName = ".Intranet";
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseClaimsTransformation(o => new ClaimsTransformer().TransformAsync(o));

            app.UseSession();
            app.UseDefaultFiles();
            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

person K7Buoy    schedule 28.10.2016    source источник
comment
Не могли бы вы выложить startup.cs?   -  person adem caglin    schedule 28.10.2016
comment
@ademcaglin сделано в редактировании выше. Я обнаружил, что dbcontext стал недоступен в классе ... что сделало бы попадание в db довольно сложным. :-) Не знаю почему.   -  person K7Buoy    schedule 28.10.2016


Ответы (1)


Вам нужно использовать IClaimsTransformer с внедрением зависимостей.

    app.UseClaimsTransformation(async (context) =>
    {
        IClaimsTransformer transformer = context.Context.RequestServices.GetRequiredService<IClaimsTransformer>();
        return await transformer.TransformAsync(context);
    });

    // Register
    services.AddScoped<IClaimsTransformer, ClaimsTransformer>();

И нужно ввести DbContext в ClaimsTransformer

public class ClaimsTransformer : IClaimsTransformer
{
    private readonly TimesheetContext _context;
    public ClaimsTransformer(TimesheetContext  dbContext)
    {
        _context = dbContext;
    }
    // ....
 }
person adem caglin    schedule 28.10.2016
comment
Контекст базы данных (_context) по-прежнему имеет значение null в классе ClaimTransformer, но это кажется правильным. - person K7Buoy; 28.10.2016
comment
Вы добавили последнюю часть моего ответа? - person adem caglin; 28.10.2016
comment
Что ж. это неловко! нет, я не сделал. Я не могу отблагодарить вас достаточно, потерянные часы на это. - person K7Buoy; 28.10.2016