¿Cómo usar la autenticación de Windows Active Directory y las notificaciones basadas en identidad?


Problema

Queremos usar Windows Active Directory para autenticar a un usuario en la aplicación. Sin embargo, no queremos usar grupos de Active Directory para administrar la autorización de controllers/views.

Por lo que yo sé, no hay una manera fácil de casar AD y reclamaciones basadas en la identidad.

Objetivos

  • Autenticar usuarios con Active Directory local
  • Use Identity framework para administrar notificaciones

Intentos (Fails)

  • Ventanas.Owin.Seguridad.ActiveDirectory-Doh. Esto es para Azure AD. Sin soporte LDAP. ¿Podrían haberlo llamado AzureActiveDirectory en su lugar?
  • Autenticación de Windows - Esto está bien con la autenticación NTLM o Keberos. Los problemas comienzan con: i) los tokens y las notificaciones son administrados por AD y no puedo averiguar cómo usar las notificaciones de identidad con él.
  • LDAP-Pero esto parece obligarme a realizar manualmente la autenticación de formularios para usar la identidad las reclamaciones? Seguramente debe haber una manera más fácil.

Cualquier ayuda sería más que apreciada. He estado atascado en este problema durante mucho tiempo y agradecería recibir información externa al respecto.

Author: hlyates, 2015-03-06

5 answers

Shoe su solución anterior me empujó hacia una dirección que funcionó para mí en MVC6-Beta3 Identityframework7-Beta3 EntityFramework7-Beta3:

// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    //
    // Check for user existance in Identity Framework
    //
    ApplicationUser applicationUser = await _userManager.FindByNameAsync(model.eID);
    if (applicationUser == null)
    {
        ModelState.AddModelError("", "Invalid username");
        return View(model);
    }

    //
    // Authenticate user credentials against Active Directory
    //
    bool isAuthenticated = await Authentication.ValidateCredentialsAsync(
        _applicationSettings.Options.DomainController, 
        _applicationSettings.Options.DomainControllerSslPort, 
        model.eID, model.Password);
    if (isAuthenticated == false)
    {
        ModelState.AddModelError("", "Invalid username or password.");
        return View(model);
    }

    //
    // Signing the user step 1.
    //
    IdentityResult identityResult 
        = await _userManager.CreateAsync(
            applicationUser, 
            cancellationToken: Context.RequestAborted);

    if(identityResult != IdentityResult.Success)
    {
        foreach (IdentityError error in identityResult.Errors)
        {
            ModelState.AddModelError("", error.Description);
        }
        return View(model);
    }

    //
    // Signing the user step 2.
    //
    await _signInManager.SignInAsync(applicationUser,
        isPersistent: false,
        authenticationMethod:null,
        cancellationToken: Context.RequestAborted);

    return RedirectToLocal(returnUrl);
}
 1
Author: Will,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-02 20:18:00

Simplemente presione AD con el nombre de usuario y la contraseña en lugar de autenticarse contra su DB

// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindByNameAsync(model.UserName);
        if (user != null && AuthenticateAD(model.UserName, model.Password))
        {
            await SignInAsync(user, model.RememberMe);
            return RedirectToLocal(returnUrl);
        }
        else
        {
            ModelState.AddModelError("", "Invalid username or password.");
        }
    }
    return View(model);
}

public bool AuthenticateAD(string username, string password)
{
    using(var context = new PrincipalContext(ContextType.Domain, "MYDOMAIN"))
    {
        return context.ValidateCredentials(username, password);
    }
}
 17
Author: Shoe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-02 20:21:55

En ASPNET5 (beta6), la idea es usar CookieAuthentication e Identity : necesitarás agregar en tu clase Startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddAuthorization();
    services.AddIdentity<MyUser, MyRole>()
        .AddUserStore<MyUserStore<MyUser>>()
        .AddRoleStore<MyRoleStore<MyRole>>()
        .AddUserManager<MyUserManager>()
        .AddDefaultTokenProviders();
}

En la sección configurar, agregue:

private void ConfigureAuth(IApplicationBuilder app)
{
    // Use Microsoft.AspNet.Identity & Cookie authentication
    app.UseIdentity();
    app.UseCookieAuthentication(options =>
    {
        options.AutomaticAuthentication = true;
        options.LoginPath = new PathString("/App/Login");
    });
}

Entonces, tendrá que implementar:

Microsoft.AspNet.Identity.IUserStore
Microsoft.AspNet.Identity.IRoleStore
Microsoft.AspNet.Identity.IUserClaimsPrincipalFactory

Y extender / anular:

Microsoft.AspNet.Identity.UserManager
Microsoft.AspNet.Identity.SignInManager

En realidad he configurado un proyecto de ejemplo para mostrar cómo se puede hacer esto. Enlace de GitHub .

Probé en el beta8 y con algunos pequeños adaptatons (como Context => HttpContext) también funcionó.

 4
Author: jesblit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-02 20:30:57

Usted podría utilizar ClaimTransformation, acabo de conseguir que funcione esta tarde utilizando el artículo y el código a continuación. Estoy accediendo a una aplicación con autenticación de ventana y luego agrego notificaciones basadas en permisos almacenados en una base de datos SQL. Este es un buen artículo que debería ayudarte.

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

En resumen ...

services.AddScoped<IClaimsTransformer, ClaimsTransformer>();

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

public class ClaimsTransformer : IClaimsTransformer
    {
        private readonly DbContext _context;

        public ClaimsTransformer(DbContext dbContext)
        {
            _context = dbContext;
        }
        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 by username
                var username = windowsIdentity.Name.Remove(0, 6);
                var appUser = _context.User.FirstOrDefault(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);
        }
    }
 3
Author: K7Buoy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-28 14:39:47

¿Sabes cómo implementar un System.Web.Security.MembershipProvider personalizado? Debería poder usar esto (override ValidateUser) junto con System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials() para autenticarse contra active directory.

Intente: var pc = new PrincipalContext(ContextType.Domain, "example.com", "DC=example,DC=com"); pc.ValidateCredentials(username, password);

 1
Author: rybl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-05 21:52:56