web-dev-qa-db-fra.com

Implémentation de la connexion Active Directory dans un projet Web ASP.NET MVC 4.6 existant

Je dois modifier le nom d'utilisateur (Windows) existant de mon application ASP.NET MVC + Knockout avec authentification Active Directory . Il est composé de contrôleurs MVC et de contrôleurs WebAPI. Les deux doivent être authentifiés.

Je pensais que je le ferais en passant à forms authentication et en créant une page de connexion. Lorsque les utilisateurs cliquent sur le bouton de connexion, interrogez Active Directory avec le System.DirectoryServices.DirectoryEntry. Les autres processus, tels que la modification du mot de passe, l'enregistrement, obtiennent également une page HTML personnalisée. et faire leurs actions via le System.DirectoryServices.DirectoryEntry sur notre Active Directory.

(c’est-à-dire que je ne pouvais trouver aucun autre moyen de le faire, et j’en ai trouvé qui le ferait de la sorte, et cela ressemble au précédent forms authentications que j’ai vu. Dans ce cas, l’utilisateur/les mots de passe ne seraient pas dans une table de base de données, mais dans Active Directory. Même idée, permuter la table de base de données par Active Directory).

Pour voir comment cela se passerait sur un nouveau projet, j'ai créé un nouveau projet ASP.NET MVC et choisi "Comptes professionnels" (qui signifie "pour les applications qui authentifient les utilisateurs avec Active Directory), puis" sur site " Cependant, je dois alors fournir ces éléments:

  • autorité sur place 
  • uRL de l'identifiant de l'application

Je ne sais pas quoi faire avec ça. La seule chose que j'ai est une URL Active Directory comme ldap://etc..

Est-ce une autre/nouvelle/meilleure façon de se connecter à Active Directory? Ou le seul qui soit correct (l'authentification par formulaires est-elle erronée?) Ou le mauvais?

Je suis confus.

7
Michel

J'ai utilisé l'approche suivante pour implémenter l'authentification Active Directory dans ASP.NET MVC 5 et cela fonctionne comme un charme:

Étape 1:Modifiez les méthodes de connexion dans la AccountController comme indiqué ci-dessous (ajoutez également les références nécessaires):

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    try
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        // Check if the User exists in LDAP
        if (Membership.GetUser(model.UserName) == null)
        {
            ModelState.AddModelError("", "Wrong username or password");
            return this.View(model);
        }

        ApplicationGroupManager groupManager = new ApplicationGroupManager();

        // Validate the user using LDAP 
        if (Membership.ValidateUser(model.UserName, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
            // FormsAuthentication.SetAuthCookie(model.UserName, false);

            // Check if the User exists in the ASP.NET Identity table (AspNetUsers)
            string userName = model.UserName.ToString().ToLower(new CultureInfo("en-US", false)); // When UserName is entered in uppercase containing "I", the user cannot be found in LDAP
            //ApplicationUser user = UserManager.FindByName(userName);
            ApplicationUser user = await UserManager.FindByNameAsync(userName); //Asynchronous method

            if (user == null) // If the User DOES NOT exists in the ASP.NET Identity table (AspNetUsers)
            {
                // Create a new user using the User data retrieved from LDAP
                // Create an array of properties that we would like and add them to the search object  
                string[] requiredProperties = new string[] { "samaccountname", "givenname", "sn", "mail", "physicalDeliveryOfficeName", "title" };
                var userInfo = CreateDirectoryEntry(model.UserName, requiredProperties);

                user = new ApplicationUser();

                // For more information about "User Attributes - Inside Active Directory" : http://www.kouti.com/tables/userattributes.htm
                user.UserName = userInfo.GetDirectoryEntry().Properties["samaccountname"].Value.ToString();
                user.Name = userInfo.GetDirectoryEntry().Properties["givenname"].Value.ToString();
                user.Surname = userInfo.GetDirectoryEntry().Properties["sn"].Value.ToString();
                user.Email = userInfo.GetDirectoryEntry().Properties["mail"].Value.ToString();
                user.EmailConfirmed = true;
                //user.PasswordHash = null;
                //user.Department = GetDepartmentId(userInfo.GetDirectoryEntry().Properties["physicalDeliveryOfficeName"].Value.ToString());

                //await Register(user);
                var result = await UserManager.CreateAsync(user); //Asynchronous method

                //If the User has succesfully been created
                //if (result.Succeeded)
                //{
                //    //var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
                //    //var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
                //    //await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
                //    //ViewBag.Link = callbackUrl;
                //    //return View("DisplayEmail");
                //}

                // Define user group (and roles)
                var defaultGroup = "751b30d7-80be-4b3e-bfdb-3ff8c13be05e"; // Id of the ApplicationGroup for the Default roles
                //groupManager.SetUserGroups(newUser.Id, new string[] { defaultGroup });
                await groupManager.SetUserGroupsAsync(user.Id, new string[] { defaultGroup }); //Asynchronous method
                //groupManager.SetGroupRoles(newGroup.Id, new string[] { role.Name });
            }
            // !!! THERE IS NO NEED TO ASSIGN ROLES AS IT IS ASSIGNED AUTOMATICALLY IN ASP.NET Identity 2.0
            //else // If the User exists in the ASP.NET Identity table (AspNetUsers)
            //{
            //    //##################### Some useful ASP.NET Identity 2.0 methods (for Info) #####################
            //    //ApplicationGroupManager gm = new ApplicationGroupManager();
            //    //string roleName = RoleManager.FindById("").Name; // Returns Role Name by using Role Id parameter
            //    //var userGroupRoles = gm.GetUserGroupRoles(""); // Returns Group Id and Role Id by using User Id parameter
            //    //var groupRoles = gm.GetGroupRoles(""); // Returns Group Roles by using Group Id parameter
            //    //string[] groupRoleNames = groupRoles.Select(p => p.Name).ToArray(); // Assing Group Role Names to a string array
            //    //###############################################################################################

            //    // Assign Default ApplicationGroupRoles to the User
            //    // As the default roles are already defined to the User after the first login to the system, there is no need to check if the role is NULL (otherwise it must be checked!!!)
            //    //var groupRoles = groupManager.GetGroupRoles("751b30d7-80be-4b3e-bfdb-3ff8c13be05e"); // Returns Group Roles by using Group Id parameter
            //    var groupRoles = await groupManager.GetGroupRolesAsync("751b30d7-80be-4b3e-bfdb-3ff8c13be05e"); // Returns Group Roles by using Group Id parameter (Asynchronous method)

            //    foreach (var role in groupRoles)
            //    {
            //        //Assign ApplicationGroupRoles to the User
            //        string roleName = RoleManager.FindById(role.Id).Name;
            //        UserManager.AddToRole(user.Id, roleName);
            //    }
            //}

            //Sign in the user
            await SignInAsync(user, model.RememberMe);

            if (this.Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                        && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
            {
                return this.Redirect(returnUrl);
                //return RedirectToLocal(returnUrl);
            }
            return this.RedirectToAction("Index", "Home");
        }
        else
        {
            ModelState.AddModelError("", "Wrong username or password");
            return this.View(model);
        }
    }
    catch (Exception ex)
    {
        TempData["ErrorMessage"] = ex.Message.ToString();
        return View("Error", TempData["ErrorMessage"]);
    }
}

/* Since ASP.NET Identity and OWIN Cookie Authentication are claims-based system, the framework requires the app to generate a ClaimsIdentity for the user. 
ClaimsIdentity has information about all the claims for the user, such as what roles the user belongs to. You can also add more claims for the user at this stage.
The highlighted code below in the SignInAsync method signs in the user by using the AuthenticationManager from OWIN and calling SignIn and passing in the ClaimsIdentity. */
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
    var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
    AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}

static SearchResult CreateDirectoryEntry(string sAMAccountName, string[] requiredProperties)
{
    DirectoryEntry ldapConnection = null;

    try
    {
        // Create LDAP connection object  
        //ldapConnection = new DirectoryEntry("alpha.company.com");
        ldapConnection = new DirectoryEntry("LDAP://OU=Company_Infrastructure, DC=company, DC=mydomain", "******", "******");
        //ldapConnection.Path = connectionPath;
        ldapConnection.AuthenticationType = AuthenticationTypes.Secure;

        DirectorySearcher search = new DirectorySearcher(ldapConnection);
        search.Filter = String.Format("(sAMAccountName={0})", sAMAccountName);

        foreach (String property in requiredProperties)
            search.PropertiesToLoad.Add(property);

        SearchResult result = search.FindOne();
        //SearchResultCollection searchResultCollection = search.FindAll();

        if (result != null)
        {
            //foreach (String property in requiredProperties)
            //    foreach (Object myCollection in result.Properties[property])
            //        Console.WriteLine(String.Format("{0,-20} : {1}",
            //                      property, myCollection.ToString()));
            // return searchResultCollection;
            return result;
        }
        else
        {
            return null;
            //Console.WriteLine("User not found!");
        }
        //return ldapConnection;
    }
    catch (Exception e)
    {
        Console.WriteLine("Exception caught:\n\n" + e.ToString());
    }
    return null;
}

Remarque:Pour forcer la déconnexion dans l'authentification LDAP, ajoutez une ligne FormsAuthentication.SignOut() à la méthode LogOff() comme indiqué ci-dessous:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
    AuthenticationManager.SignOut();
    FormsAuthentication.SignOut(); //In order to force logout in LDAP authentication
    return RedirectToAction("Login", "Account");
}


Étape 2:Mettez à jour votre LoginViewModel (ou le nom de votre classe de modèle de compte) pour ne contenir que cette classe LoginModel:

public class LoginViewModel
{
    [Required]
    public string UserName { get; set; }

    [Required]
    [EmailAddress]
    public string Email { get; set; }

    [Required]
    [DataType(DataType.Password)]
    public string Password { get; set; }

    public bool RememberMe { get; set; }
}

D'autre part, ajoutez les propriétés personnalisées, à savoir Nom, Nom, Nom d'utilisateur, Département, etc. au modèle nécessaire, à savoir ApplicationUser, RegisterViewModel.


Étape 3:Enfin, mettez à jour votre fichier Web.config pour inclure ces éléments:

<connectionStrings>
  <!-- for LDAP -->
  <add name="ADConnectionString" connectionString="LDAP://**.**.***:000/DC=abc,DC=xyz" />
</connectionStrings>

<system.web>
  <!-- For LDAP -->
  <httpCookies httpOnlyCookies="true" />
  <authentication mode="Forms">
    <forms name=".ADAuthCookie" loginUrl="~/Account/Login" timeout="30" slidingExpiration="true" protection="All" />
  </authentication>
  <membership defaultProvider="ADMembershipProvider">
    <providers>
      <clear />
      <add name="ADMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider" connectionStringName="ADConnectionString" attributeMapUsername="sAMAccountName" connectionUsername="******" connectionPassword="******" />
    </providers>
  </membership>

  ...
</system.web>


J'espère que cela t'aides...

3
Murat Yıldız

J'ai utilisé Active Directory Authetication au travail . J'ai créé l'application MVC avec Windows Authetication et cela a été fait. L'application affiche automatiquement AD login avec le domaine. Choisissez l'appartenance: [Authorize(Roles=@"DomainName\GroupName")]Vous pouvez voir le domaine et les groupes dans cmd: Nom d'utilisateur Net /domainVous ne devez pas utiliser LDAP.

Avec LDAP voir: voir ici

2
mrsWhite

j'ai un système mixte en cours d'exécution. Utilisateurs d'une base de données (utilisateurs externes) mélangés à des utilisateurs AD (utilisateurs internes) pouvant se connecter à notre système. Pour communiquer avec AD, j'utilise un package de nuget appelé LinqToLdap ( https://github.com/madhatter22/LinqToLdap ). cela utilise le protocole LDAP afin qu'il puisse également être utilisé pour l'authentification auprès des serveurs Unix Ldap.

c'est la méthode d'authentification

public bool AuthenticateUser(string userName, string password)
    {
        InitConfig();
        using (var context = new DirectoryContext(_config))
        {
            var user = context.Query<LdapUserInfo>().FirstOrDefault(x => x.UserPrincipalName.Equals(userName));
            var dn = user?.DistinguishedName;
            if (string.IsNullOrWhiteSpace(dn))
                return false;

            using (var ldap = new LdapConnection(new LdapDirectoryIdentifier(_myConfig.Server)))
            {
                ldap.SessionOptions.ProtocolVersion = 3;

                ldap.AuthType = AuthType.Basic;
                ldap.Credential = _credentials;
                ldap.Bind();

                try
                {
                    ldap.AuthType = AuthType.Basic;
                    ldap.Bind(new NetworkCredential(dn, password));
                    return true;
                }
                catch (DirectoryOperationException)
                { }
                catch (LdapException)
                { }
            }

            return false;
        }
    }

    private void InitConfig()
    {
        if (_config != null)
            return;

        _config = new LdapConfiguration();
        _credentials = new NetworkCredential(_myConfig.Username, _myConfig.Password, _myConfig.Domain);
        _config.AddMapping(new AutoClassMap<LdapGroupInfo>(), _myConfig.NamingContext, new[] { "*" });
        _config.AddMapping(new AutoClassMap<LdapUserInfo>(), _myConfig.NamingContext, new[] { "*" });

        _config.ConfigureFactory(_myConfig.Server).AuthenticateAs(_credentials).AuthenticateBy(AuthType.Basic);
    }
1
Gelootn