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



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

我想我会通过更改为forms authentication并创建一个登录页面来做到这一点,当用户单击登录时,使用System.DirectoryServices.DirectoryEntry查询活动目录。 然后,其他进程(如更改密码,注册等(也将获得一个自定义的html页面,并通过Active Directory上的System.DirectoryServices.DirectoryEntry进行操作。

(也就是说,我找不到人们会这样做的任何其他方法,我确实找到了一些人会这样做,这听起来和我见过的前forms authentication一样。在这种情况下,用户/密码将不在数据库表中,而是在活动目录中。同样的想法,通过活动目录交换数据库表(。

为了了解这在一个全新的项目中会如何,我创建了一个新的 ASP.NET MVC 项目并选择"工作或学校计数"(它说"对于使用活动目录对用户进行身份验证的应用程序"(并选择"内部部署"。 但是,我必须提供以下项目:

  • 本地管理机构
  • 应用 ID 网址

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

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

我很困惑。

您可以使用以下方法在ASP.NET MVC中实现Active Directory身份验证。

步骤 1:修改AccountController中的登录方法,如下所示(同时添加必要的引用(:

[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:nn" + 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>

更新:下面是示例中使用ApplicationUser类:

// Must be expressed in terms of our custom Role and other types:
public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, 
ApplicationUserRole, ApplicationUserClaim>, IUser<int>
{
public string Name { get; set; }
public string Surname { get; set; }

public async Task<ClaimsIdentity>
GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
{
var userIdentity = await manager
.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
}

我在工作中用过Active Directory Authetication。 我用Windows Authetication创建了应用程序MVC并完成了。应用程序会自动显示AD域登录。 选择会员资格:[Authorize(Roles=@"DomainNameGroupName")]叶可以在cmd中看到域和组:网络用户用户名/域 您不必使用LDAP.

LDAP一起查看: 看这里

我有一个混合系统正在运行。来自数据库的用户(外部用户(与可以登录我们系统的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);
}

最新更新