在现有的ASP.NET MVC 4.6 Web项目中实现Active Directory登录

时间:2018-08-21 15:38:02

标签: asp.net-mvc login active-directory

我必须使用Active Directory身份验证更改ASP.NET MVC + Knockout应用程序的现有(Windows)登录名。 它由mvc控制器和webapi控制器组成。两者都必须经过验证。

我想我可以通过更改为forms authentication并创建一个登录页面来完成此操作,当用户单击登录时,请使用System.DirectoryServices.DirectoryEntry查询Active Directory。 然后,其他过程(例如更改密码,注册等)也将获得自定义html页面,并通过我们的Active Directory上的System.DirectoryServices.DirectoryEntry进行操作。

(也就是说,我找不到其他人可以做到的方式,而我确实找到了一些可以做到这一点的人,这听起来和以前的forms authentication一样在这种情况下,用户名/密码将不在数据库表中,而在Active Directory中。相同的想法是,按活动目录交换数据库表)。

要查看在全新项目中的情况,我创建了一个新的ASP.NET MVC项目,然后选择“工作或学校帐户”(对于“使用活动目录对用户进行身份验证的应用程序说”),然后选择“本地” '。 但是,然后我必须提供以下项目:

  • 内部授权
  • 应用ID网址

我不知道该怎么办。我唯一拥有的是一个活动目录网址,例如ldap://etc.

这是进行活动目录登录的另一种/较新的/更好的方法吗?还是唯一正确的方法(表单身份验证是错误的?)还是错误的方法?

我很困惑。

3 个答案:

答案 0 :(得分:4)

为了在ASP.NET MVC 5中实现Active Directory身份验证,我使用了以下方法,并且这种方法很吸引人:

步骤1: 如下所示修改AccountController中的Login方法(还添加必要的引用):

[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;
}

注意: :为了强制进行LDAP身份验证,请在FormsAuthentication.SignOut()方法中添加LogOff()行,如下所示:

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


第2步: :将您的LoginViewModel(或任何您的Account模型类命名)更新为仅包含此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; }
}

另一方面,将自定义属性(例如名称,姓氏,用户名,部门等)添加到必要的模型中,例如ApplicationUserRegisterViewModel


第3步: :最后,更新您的Web.config文件以包括以下元素:

<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>


希望这会有所帮助...

答案 1 :(得分:2)

我在工作中使用过Active Directory Authetication。 我使用MVC创建了应用程序Windows Authetication,并已完成。应用程序自动显示AD登录域。 选择成员资格:[Authorize(Roles=@"DomainName\GroupName")] 您可以在cmd中查看域和组:净用户用户名/ domain 您没有使用LDAP

使用LDAP,请参见: see here

答案 2 :(得分:1)

我正在运行混合系统。来自数据库的用户(外部用户)与可以登录我们系统的AD用户(内部用户)混合在一起。为了与AD通信,我使用一个名为LinqToLdap(https://github.com/madhatter22/LinqToLdap)的nuget程序包。它使用LDAP协议,因此也可以用于针对Unix Ldap服务器进行身份验证。

这是身份验证方法

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