web-dev-qa-db-fra.com

Comment utiliser l'authentification Windows Active Directory et les revendications basées sur l'identité?

Problème

Nous voulons utiliser Windows Active Directory pour authentifier un utilisateur dans l'application. Cependant, nous ne voulons pas utiliser de groupes Active Directory pour gérer l'autorisation des contrôleurs/vues.

Pour autant que je sache, il n'y a pas de moyen facile de marier AD et revendications basées sur l'identité.

Buts

  • Authentifier les utilisateurs avec Active Directory local
  • Utiliser le cadre d'identité pour gérer les réclamations

Tentatives (échoue)

  • Windows.Owin.Security.ActiveDirectory - Doh. C'est pour Azure AD. Pas de support LDAP. Auraient-ils pu l'appeler AzureActiveDirectory à la place?
  • Authentification Windows - Ceci est correct avec l'authentification NTLM ou Keberos. Les problèmes commencent par: i) les jetons et les revendications sont tous gérés par AD et je ne peux pas comprendre comment utiliser les revendications d'identité avec.
  • LDAP - Mais ceux-ci semblent m'obliger à faire manuellement l'authentification par formulaire afin d'utiliser des revendications d'identité? Il doit sûrement y avoir un moyen plus simple?

Toute aide serait plus qu'appréciée. Je suis coincé sur ce problème depuis assez longtemps et j'apprécierais une contribution extérieure sur la question.

29
hlyates

Shoe votre solution ci-dessus m'a poussé vers une direction qui a fonctionné pour moi sur 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);
}
2
Will

Appuyez simplement sur AD avec le nom d'utilisateur et le mot de passe au lieu de vous authentifier sur votre base de données

// 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);
    }
}
20
Shoe

Sur ASPNET5 (beta6), l'idée est d'utiliser CookieAuthentication et Identity: vous devrez ajouter dans votre classe de démarrage:

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

Dans la section de configuration, ajoutez:

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");
    });
}

Ensuite, vous devrez implémenter:

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

et étendre/remplacer:

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

J'ai en fait configuré un exemple de projet pour montrer comment cela peut être fait. Lien GitHub .

J'ai testé sur la beta8 et avec quelques petites adaptations (comme Context => HttpContext) cela a fonctionné aussi.

4
jesblit

Vous pouvez utiliser ClaimTransformation, je viens de le faire fonctionner cet après-midi en utilisant l'article et le code ci-dessous. J'accède à une application avec l'authentification Windows, puis j'ajoute des revendications basées sur des autorisations stockées dans une base de données SQL. Ceci est un bon article qui devrait vous aider.

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

En résumé ...

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
K7Buoy

Savez-vous comment implémenter un System.Web.Security.MembershipProvider Personnalisé? Vous devriez pouvoir utiliser ceci (remplacer ValidateUser) conjointement avec System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials() pour vous authentifier auprès d'Active Directory.

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

1
rybl