web-dev-qa-db-ja.com

既存のASP.NET MVC 4.6 WebプロジェクトにActive Directoryログインを実装する

ASP.NET MVC + Knockoutアプリケーションの既存の(Windows)ログインをActive Directory認証で変更する必要があります。 mvcコントローラーとwebapiコントローラーで構成されています。両方とも認証される必要があります。

forms authenticationに変更してログインページを作成し、ユーザーがログインをクリックすると、System.DirectoryServices.DirectoryEntryを使用してActive Directoryを照会することでこれを行うと考えました。次に、パスワードの変更、登録などの他のプロセスもカスタムhtmlページを取得し、Active Directory上のSystem.DirectoryServices.DirectoryEntryを介してアクションを実行します。

(つまり、私は人々がそれを行う他の方法を見つけることができませんでした、そして私はこのようにする人を見つけました、そしてそれは以前のforms authenticationsと同じように聞こえますこの場合、ユーザー/パスワードはデータベーステーブルではなく、Active Directoryにあります。同じ考え、Active Directoryでデータベーステーブルをスワップします)。

これが新しいプロジェクトでどのようになるかを確認するために、新しいASP.NET MVCプロジェクトを作成し、「work-or school acounts」(「Active Directoryでユーザーを認証するアプリケーションの場合」と言う)を選択し、「on premise」を選択します。ただし、これらのアイテムを提供する必要があります。

  • オンプレミス機関
  • アプリIDのURL

どうすればいいのかわかりません。私が持っている唯一のものは、ldap://etc.のようなActive DirectoryのURLです。

これは、Active Directoryログインを行うための別の/新しい/より良い方法ですか?または唯一の正しいもの(フォーム認証は間違っていますか?)または間違ったものですか?

よくわかりません。

10
Michel

_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(またはアカウントモデルクラスの名前)を更新して、この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>
_


お役に立てれば...

6
Murat Yıldız

私は職場で_Active Directory Authetication_を使用しました。 _Windows Authetication_を使用してアプリケーションMVCを作成しました。アプリケーションは、ドメインでのADログインを自動的に表示します。メンバーシップを選択します:[Authorize(Roles=@"DomainName\GroupName")]あなたはcmdでドメインとグループを見ることができます:ネットユーザーユーザー名/ domain LDAPを使用する必要はありません。

LDAP参照: ここを参照

2
mrsWhite

私は混合システムを実行しています。システムにログインできるADユーザー(内部ユーザー)と混在するデータベースのユーザー(外部ユーザー)。 ADと通信するには、LinqToLdapと呼ばれるナゲットパッケージを使用します( https://github.com/madhatter22/LinqToLdap )。これは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);
    }
1
Gelootn