В результате запроса Active Directory из MVC: Попытка доступа к незагруженному домену приложения.(Исключение из HRESULT: 0x80131014) - PullRequest
26 голосов
/ 07 июля 2011

У меня проблема с использованием c # в .Net 4 в веб-приложении MVC, когда при запросе Active Directory я часто получаю сообщение об ошибке: Попытка доступа к незагруженному домену приложения , (Исключение из HRESULT: 0x80131014).

Странно то, что какое-то время он будет работать безупречно, а потом он просто начнет происходить, а потом просто снова исчезнет.

Я сделал несколько модификаций функции, чтобы она заработала, но все они, похоже, не работают. Мне интересно, если я делаю что-то не так или есть лучший способ сделать это.

Вот моя текущая функция, которая будет принимать loginId и PrincipalContext. LoginId может быть пользователем DisplayName , т. Е. "John Smith" или DOMAINNAME \ josmi . По умолчанию используются первые 2 буквы их имени, а затем первые 3 буквы их фамилии. Там есть проверка, если это не так. Эта часть, если хорошо.

public List<ADGroup> GetMemberGroups(string loginId, PrincipalContext principalContext, int tries = 0)
{
    var result = new List<ADGroup>();

    try
    {
        var samAccountName = "";
        if (loginId.Contains(" "))
        {
            var fName = loginId.Split(Char.Parse(" "))[0].ToLower();
            var sName = loginId.Split(Char.Parse(" "))[1].ToLower();

            if (sName.Trim().Length == 2)
                samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 4) : fName.Substring(0, 3), sName.Substring(0, 2));
            else
                samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 3) : fName.Substring(0, 2), sName.Substring(0, 3));
        }
        else
            samAccountName = loginId.Substring(loginId.IndexOf(@"\") + 1);

        var authPrincipal = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, samAccountName);

        if (authPrincipal == null)
            throw new Exception(string.Format("authPrincipal is null for loginId - {0}", loginId));

        var firstLevelGroups = authPrincipal.GetGroups();

        AddGroups(firstLevelGroups, ref result);
    }
    catch
    {
        if (tries > 5)
            throw;

        tries += 1;

        System.Threading.Thread.Sleep(1000);
        GetMemberGroups(loginId, principalContext, tries);
    }

    return result;
}

    private void AddGroups(PrincipalSearchResult<Principal> principal, ref List<ADGroup> returnList)
    {
        foreach (var item in principal)
        {
            if (item.GetGroups().Count() > 0)
                AddGroups(item.GetGroups(), ref returnList);

            returnList.Add(new ADGroup(item.SamAccountName, item.Sid.Value));
        }
    }

Эта функция вызывается так:

MembershipGroups = ad.GetMemberGroups(user.SamAccountName, new PrincipalContext(ContextType.Domain));

Ошибка, которую я ИНОГДА получаю:

System.AppDomainUnloadedException: Попытка получить доступ к незагруженному AppDomain. (Исключение из HRESULT: 0x80131014) в System.StubHelpers.StubHelpers.InternalGetCOMHRExceptionObject (Int32 hr, IntPtr pCPCMD, объект pThis) в System.StubHelpers.StubHelpers.GetCOMHRExceptionObject (Int32 hr, IntPtr pCPCMD, объект pThis) в System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IADsPathname.Retrieve (Int32 lnFormatType) в System.DirectoryServices.AccountManagement.ADStoreCtx.LoadDomainInfo () в System.DirectoryServices.AccountManagement.ADStoreCtx.get_UserSuppliedServerName () в System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.BuildPathFromDN (String дн) в System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.MoveNextPrimaryGroupDN () в System.DirectoryServices.AccountManagement.ADDNLinkedAttrSet.MoveNext () в System.DirectoryServices.AccountManagement.FindResultEnumerator 1.MoveNext() at System.DirectoryServices.AccountManagement.FindResultEnumerator 1.System.Collections.IEnumerator.MoveNext ()

Ответы [ 5 ]

9 голосов
/ 23 июля 2011

смотря на рефлектор System.DirectoryServices.AccountManagement, внутренний класс "UnsafeNativeMethods" реализован в собственном коде, поэтому UserSuppliedServerName на один уровень выше - это все, что я могу продолжить, не глядя на виртуальную машину CLR (честно говоря, я даже не уверен, как сделать это) Кажется, что узел не может вернуть свою основную группу, так что, возможно, рассмотрим другие реализации, после небольшого поиска в Google, которые могут помочь

  • Active Directory и вложенные группы эта может быть многообещающей вот пример кода ..

        public IList<string> FindUserGroupsLdap(string username)
    { 
        // setup credentials and connection  
        var credentials = new NetworkCredential("username", "password", "domain");  
        var ldapidentifier = new LdapDirectoryIdentifier("server", 389, true, false); 
        var ldapConn = new LdapConnection(ldapidentifier, credentials); 
        // retrieving the rootDomainNamingContext, this will make sure we query the absolute root   
        var getRootRequest = new SearchRequest(string.Empty, "objectClass=*", SearchScope.Base, "rootDomainNamingContext");  
        var rootResponse = (SearchResponse)ldapConn.SendRequest(getRootRequest);   
        var rootContext = rootResponse.Entries[0].Attributes["rootDomainNamingContext"][0].ToString();  
        // retrieve the user 
        string ldapFilter = string.Format("(&(objectCategory=person)(sAMAccountName={0}))", username); 
        var getUserRequest = new SearchRequest(rootContext, ldapFilter, SearchScope.Subtree, null);  
        var userResponse = (SearchResponse)ldapConn.SendRequest(getUserRequest);     
        // send a new request to retrieve the tokenGroups attribute, we can not do this with our previous request since 
        // tokenGroups needs SearchScope.Base (dont know why...)  
        var tokenRequest = new SearchRequest(userResponse.Entries[0].DistinguishedName, "(&(objectCategory=person))", SearchScope.Base, "tokenGroups");  
        var tokenResponse = (SearchResponse)ldapConn.SendRequest(tokenRequest);  
        var tokengroups = tokenResponse.Entries[0].Attributes["tokenGroups"].GetValues(typeof(byte[])); 
        // build query string this query will then look like (|(objectSid=sid)(objectSid=sid2)(objectSid=sid3))  
        // we need to convert the given bytes to a hexadecimal representation because thats the way they   
        // sit in ActiveDirectory  
        var sb = new StringBuilder();  
        sb.Append("(|");   
        for (int i = 0; i < tokengroups.Length; i++)  
        {  
            var arr = (byte[])tokengroups[i];    
            sb.AppendFormat("(objectSid={0})", BuildHexString(arr));   
        }   
        sb.Append(")");    
        // send the request with our build query. This will retrieve all groups with the given objectSid
        var groupsRequest = new SearchRequest(rootContext, sb.ToString(), SearchScope.Subtree, "sAMAccountName"); 
        var groupsResponse = (SearchResponse)ldapConn.SendRequest(groupsRequest); 
        // loop trough and get the sAMAccountName (normal, readable name)   
        var userMemberOfGroups = new List<string>();    
        foreach (SearchResultEntry entry in groupsResponse.Entries)    
        userMemberOfGroups.Add(entry.Attributes["sAMAccountName"][0].ToString());  
        return userMemberOfGroups;
    } 
    
    private string BuildHexString(byte[] bytes)
    {  
        var sb = new StringBuilder(); 
        for (int i = 0; i < bytes.Length; i++) 
        sb.AppendFormat("\\{0}", bytes[i].ToString("X2")); 
        return sb.ToString();
    }
    

Это больше для информационных целей

5 голосов
/ 11 апреля 2017

Я не знаю, как здесь передается PrincipalContext, но одна вещь, которую я заметил в своем собственном коде и исследовании, когда у меня была эта ошибка, у меня была:

PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain);
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext , strUserName);

Где strUserName был какой-то пользователь, т.е. DOMAIN\johndoe

Я вызывал этот код (который был в отдельной функции) и возвращал объект UserPrincipal как up и передавал его:

using (PrincipalSearchResult<Principal> result = up.GetGroups())
{
    // do something with result, here
}

result не будет нулевым, но после того, как я проверил это условие, я проверил, если result.Count() > 0, и вот когда это не получится (иногда - хотя я мог бы воссоздать условия, когда это произойдет, нажав на определенной вкладке в моем приложении, которая называла этот код - хотя тот же код был вызван как загрузка моего приложения и не было проблем). Свойство Message в result было Attempted to access an unloaded appdomain. (Exception from HRESULT: 0x80131014).

Я нашел в аналогичную запись , на которую я должен был указать домен в PrincipalContext. Поскольку я не мог жестко написать мой код, поскольку мы перемещали наш код между средами Dev, Test и Production, где у них разные домены для каждого из них, я смог указать его как Environment.UserDomainName:

PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain, Environment.UserDomainName);

Это избавило меня от ошибки.

4 голосов
/ 28 ноября 2012

Эта проблема аналогична Определите, находится ли пользователь в группе AD для приложения .NET 4.0

Кажется, это ошибка в ADSI, которая была исправлена ​​с помощью исправления. Windows 7 с пакетом обновления 1 и Windows Server 2008 R2 с пакетом обновления 1 (SP1) не содержат исправления, поэтому его необходимо будет вручную развернуть на компьютерах разработчика и в серверных средах.

http://support.microsoft.com/kb/2683913

0 голосов
/ 25 июля 2011

1001 * попробовать *

public List<ADGroup> GetMemberGroups(string loginId, PrincipalContext principalContext, int tries = 0)
{

var result = new List<ADGroup>();
bool Done = false;

try
{
    var samAccountName = "";
    if (loginId.Contains(" "))
    {
        var fName = loginId.Split(Char.Parse(" "))[0].ToLower();
        var sName = loginId.Split(Char.Parse(" "))[1].ToLower();

        if (sName.Trim().Length == 2)
            samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 4) : fName.Substring(0, 3), sName.Substring(0, 2));
        else
            samAccountName = string.Format("{0}{1}", fName.StartsWith(".") ? fName.Substring(0, 3) : fName.Substring(0, 2), sName.Substring(0, 3));
    }
    else
        samAccountName = loginId.Substring(loginId.IndexOf(@"\") + 1);

    var authPrincipal = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, samAccountName);

    if (authPrincipal == null)
        throw new Exception(string.Format("authPrincipal is null for loginId - {0}", loginId));

    var firstLevelGroups = authPrincipal.GetGroups();

    AddGroups(firstLevelGroups, ref result);

    Done = true;
}
catch
{
    if (tries > 5)
        throw;

    tries += 1;
}

if ( ( !Done) && (tries < 6) )
     {
     System.Threading.Thread.Sleep(1000);
     result = GetMemberGroups(loginId, principalContext, tries);
     }

return result;
}

private void AddGroups(PrincipalSearchResult<Principal> principal, ref List<ADGroup> returnList)
{
    if ( principal == null )
         return;
    foreach (var item in principal)
    {
        if (item.GetGroups().Count() > 0)
            AddGroups(item.GetGroups(), ref returnList);

        returnList.Add(new ADGroup(item.SamAccountName, item.Sid.Value));
    }
}

Когда происходит исключение, вы снова вызываете функцию из блока catch (в зависимости от значения попыток), но отбрасывает возвращаемое значение - так что даже если второй / третий вызов ... сработал, вы вернули пустой результат для первоначального абонента . Я изменил это, чтобы результат больше не отбрасывался ...

Во второй функции вы никогда не проверяли основной параметр на ноль перед запуском foreach ... Я тоже это изменил ...

И я удалил рекурсию из улова блока catch (хотя я действительно не уверен, имеет ли это изменение какое-либо реальное влияние).

0 голосов
/ 07 июля 2011

Вы можете добавить некоторые записи, чтобы сузить проблему. Это Thread.Sleep не выглядит так, как хотелось бы в веб-приложении:)

Если вы получаете исключения, возможно, вы могли бы обработать их по-другому.

Я считаю, что ваш домен приложений перерабатывается, пока AD занимается вуду. Добавление записи в Application_End также может дать некоторые подсказки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...