Как получить группы пользователя в Active Directory? (c#, asp.net)
Я использую этот код, чтобы получить группы текущего пользователя. Но я хочу вручную дать пользователю, а затем получить его группы. Как я могу это сделать?
using System.Security.Principal;
public ArrayList Groups()
{
ArrayList groups = new ArrayList();
foreach (IdentityReference group in System.Web.HttpContext.Current.Request.LogonUserIdentity.Groups)
{
groups.Add(group.Translate(typeof(NTAccount)).ToString());
}
return groups;
}
7 ответов:
если вы находитесь на .NET 3.5 или выше, вы можете использовать новый
System.DirectoryServices.AccountManagement
(S.DS.AM) пространство имен, которое делает это намного проще, чем раньше.читать все об этом здесь:
управление участниками безопасности каталогов в .NET Framework 3.5обновление: старые статьи журнала MSDN больше не в сети, к сожалению-вам нужно будет загрузите CHM для журнала MSDN за январь 2008 года Microsoft и читать вот эта статья.
в принципе, вам нужно иметь "основной контекст" (обычно ваш домен), Принципал пользователя, а затем вы очень легко получаете его группы:
public List<GroupPrincipal> GetGroups(string userName) { List<GroupPrincipal> result = new List<GroupPrincipal>(); // establish domain context PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain); // find your user UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, userName); // if found - grab its groups if(user != null) { PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups(); // iterate over all groups foreach(Principal p in groups) { // make sure to add only group principals if(p is GroupPrincipal) { result.Add((GroupPrincipal)p); } } } return result; }
и это все, что есть! Теперь у вас есть результат (список) групп авторизации, к которым принадлежит пользователь - повторите их, распечатайте их имена или все, что вам нужно сделать.
обновление: для доступа к определенным свойствам, которые не отображаются на
UserPrincipal
объект, вам нужно копаться в базовомDirectoryEntry
:public string GetDepartment(Principal principal) { string result = string.Empty; DirectoryEntry de = (principal.GetUnderlyingObject() as DirectoryEntry); if (de != null) { if (de.Properties.Contains("department")) { result = de.Properties["department"][0].ToString(); } } return result; }
обновление #2: кажется, не должно быть слишком сложно собрать эти два фрагмента кода вместе.... но ладно-вот оно:
public string GetDepartment(string username) { string result = string.Empty; // if you do repeated domain access, you might want to do this *once* outside this method, // and pass it in as a second parameter! PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain); // find the user UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, username); // if user is found if(user != null) { // get DirectoryEntry underlying it DirectoryEntry de = (user.GetUnderlyingObject() as DirectoryEntry); if (de != null) { if (de.Properties.Contains("department")) { result = de.Properties["department"][0].ToString(); } } } return result; }
GetAuthorizationGroups()
не находит вложенных групп. Чтобы действительно получить все группы, членом которых является данный пользователь (включая вложенные группы), попробуйте следующее:using System.Security.Principal private List<string> GetGroups(string userName) { List<string> result = new List<string>(); WindowsIdentity wi = new WindowsIdentity(userName); foreach (IdentityReference group in wi.Groups) { try { result.Add(group.Translate(typeof(NTAccount)).ToString()); } catch (Exception ex) { } } result.Sort(); return result; }
Я использую
try/catch
потому что у меня были некоторые исключения с 2 из 200 групп в очень большом объявлении, потому что некоторые SIDs больше не были доступны. (ТегTranslate()
вызов выполняет преобразование SID - > Name.)
прежде всего, GetAuthorizationGroups () - отличная функция, но, к сожалению, имеет 2 недостатка:
- производительность низкая, особенно в большой компании со многими пользователями и группами. Он извлекает намного больше данных, чем вам действительно нужно, и делает вызов сервера для каждой итерации цикла в результате
- Он содержит ошибки, которые могут привести к тому, что ваше приложение перестанет работать "когда-нибудь", когда группы и пользователи развиваются. Microsoft признала эту проблему и связана ошибка, которую вы получите, - это "произошла ошибка при перечислении групп"
поэтому я написал небольшую функцию для замены GetAuthorizationGroups () с лучшей производительностью и безопасностью ошибок. Он делает только 1 вызов LDAP с запросом, используя индексированные поля. Он может быть легко расширен, если вам нужно больше свойств, чем только имена групп (свойство"cn").
// Usage: GetAdGroupsForUser2("domain\user") or GetAdGroupsForUser2("user","domain") public static List<string> GetAdGroupsForUser2(string userName, string domainName = null) { var result = new List<string>(); if (userName.Contains('\') || userName.Contains('/')) { domainName = userName.Split(new char[] { '\', '/' })[0]; userName = userName.Split(new char[] { '\', '/' })[1]; } using (PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domainName)) using (UserPrincipal user = UserPrincipal.FindByIdentity(domainContext, userName)) using (var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domainContext.Name))) { searcher.Filter = String.Format("(&(objectCategory=group)(member={0}))", user.DistinguishedName); searcher.SearchScope = SearchScope.Subtree; searcher.PropertiesToLoad.Add("cn"); foreach (SearchResult entry in searcher.FindAll()) if (entry.Properties.Contains("cn")) result.Add(entry.Properties["cn"][0].ToString()); } return result; }
в объявлении у каждого пользователя есть свойство
memberOf
. Эта содержит список всех групп, к которым он принадлежит.вот небольшой пример кода:
// (replace "part_of_user_name" with some partial user name existing in your AD) var userNameContains = "part_of_user_name"; var identity = WindowsIdentity.GetCurrent().User; var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>(); var allSearcher = allDomains.Select(domain => { var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name)); // Apply some filter to focus on only some specfic objects searcher.Filter = String.Format("(&(&(objectCategory=person)(objectClass=user)(name=*{0}*)))", userNameContains); return searcher; }); var directoryEntriesFound = allSearcher .SelectMany(searcher => searcher.FindAll() .Cast<SearchResult>() .Select(result => result.GetDirectoryEntry())); var memberOf = directoryEntriesFound.Select(entry => { using (entry) { return new { Name = entry.Name, GroupName = ((object[])entry.Properties["MemberOf"].Value).Select(obj => obj.ToString()) }; } }); foreach (var item in memberOf) { Debug.Print("Name = " + item.Name); Debug.Print("Member of:"); foreach (var groupName in item.GroupName) { Debug.Print(" " + groupName); } Debug.Print(String.Empty); } }
в моем случае единственный способ, которым я мог бы продолжать использовать GetGroups() без каких-либо expcetion было добавление пользователя (USER_WITH_PERMISSION) в группу, которая имеет разрешение на чтение AD (Active Directory). Чрезвычайно важно построить PrincipalContext, передающий этого пользователя и пароль.
var pc = new PrincipalContext(ContextType.Domain, domain, "USER_WITH_PERMISSION", "PASS"); var user = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, userName); var groups = user.GetGroups();
шаги, которые вы можете выполнить внутри Active Directory, чтобы заставить его работать:
- в Active Directory создайте группу (или возьмите ее) и на вкладке Security добавьте "Группа Авторизации Доступа Windows"
- нажмите на кнопку "Дополнительно"
- выберите " Windows Authorization Access Group "и нажмите на кнопку"вид"
- Проверьте "читать tokenGroupsGlobalAndUniversal"
- найдите нужного пользователя и добавьте в группу, которую вы создали (взяли) с первого шага
В случае, если Translate работает локально, но не удаленно e.I group. Translate(typeof (NTAccount)
Если вы хотите, чтобы код приложения выполнялся с использованием зарегистрированного удостоверения пользователя, включите олицетворение. Олицетворение может быть включено через IIS или с помощью добавление следующего элемента в интернете.конфигурации.
<system.web> <identity impersonate="true"/>
Если олицетворение включено, приложение выполняется с использованием разрешений, найденных в учетной записи пользователя. Поэтому, если регистрируется у пользователя есть доступ к определенному сетевому ресурсу, только тогда он сможет получить доступ к этому ресурсу через приложение.
спасибо PRAGIM tech за эту информацию из его старательного видео
проверка подлинности Windows в asp.net часть 87:
https://www.youtube.com/watch?v=zftmaZ3ySMc
но олицетворение создает много накладных расходов на сервере
лучшее решение, чтобы позволить пользователям определенной сети группы это запретить анонимность в веб-конфигурации
<authorization><deny users="?"/><authentication mode="Windows"/>
и в вашем коде позади, предпочтительно в глобальном.асакс, используй HttpContext.Текущий.Пользователь.Метод isinrole:
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs) If HttpContext.Current.User.IsInRole("TheDomain\TheGroup") Then //code to do when user is in group End If
Примечание: группа должна быть записана с обратной косой чертой \ т. е. "TheDomain\TheGroup"
это работает для меня
public string[] GetGroupNames(string domainName, string userName) { List<string> result = new List<string>(); using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, domainName)) { using (PrincipalSearchResult<Principal> src = UserPrincipal.FindByIdentity(principalContext, userName).GetGroups()) { src.ToList().ForEach(sr => result.Add(sr.SamAccountName)); } } return result.ToArray(); }