【问题标题】:How can I convert from a SID to an account name in C#如何在 C# 中将 SID 转换为帐户名
【发布时间】:2009-01-31 15:56:52
【问题描述】:

我有一个扫描目录并收集一些信息的 C# 应用程序。我想显示每个文件的帐户名称。我可以通过获取 FileInfo 对象的 SID 在本地系统上执行此操作,然后执行以下操作:

string GetNameFromSID( SecurityIdentifier sid )
{
    NTAccount ntAccount = (NTAccount)sid.Translate( typeof( NTAccount ) );
    return ntAccount.ToString();
}

但是,这不适用于网络上的文件,大概是因为 Translate() 函数仅适用于本地用户帐户。我想也许我可以对 SID 进行 LDAP 查找,所以我尝试了以下方法:

string GetNameFromSID( SecurityIdentifier sid )
{
    string str = "LDAP://<SID=" + sid.Value + ">";
    DirectoryEntry dirEntry = new DirectoryEntry( str );
    return dirEntry.Name;
}

这似乎会起作用,因为对“dirEntry.Name”的访问会挂起几秒钟,好像它正在关闭并查询网络,但随后它会抛出 System.Runtime.InteropServices.COMException

有谁知道我如何获取任意文件或 SID 的帐户名?我不太了解网络或 LDAP 或任何东西。有一个名为 DirectorySearcher 的类,也许我应该使用它,但它需要一个域名,我也不知道如何获得它——我所拥有的只是我正在扫描的目录的路径。

提前致谢。

【问题讨论】:

  • 是否有 COMException 显示的特定消息?
  • 可能是组策略允许您在本地遍历目录服务,这就是您收到 COM 异常的原因。错误信息显示什么?尝试在您访问它的网络机器上运行 filemon 并查看结果。
  • 我的意思是“组策略不允许...”

标签: c# sid


【解决方案1】:

请看这里以获得好的答案:

The best way to resolve display username by SID?

要点如下:

string sid="S-1-5-21-789336058-507921405-854245398-9938";
string account = new System.Security.Principal.SecurityIdentifier(sid).Translate(typeof(System.Security.Principal.NTAccount)).ToString();

这种方法对我来说适用于活动目录上的非本地 SID。

【讨论】:

    【解决方案2】:

    SecurityReference 对象的 Translate 方法确实适用于非本地 SID,但仅适用于域帐户。对于另一台机器的本地帐户或非域设置中的帐户,您需要 PInvoke 函数 LookupAccountSid 指定需要执行查找的特定机器名称。

    【讨论】:

    • 我刚刚开始编写一些代码来做类似的事情,并发现这是正确的。 P/Invoking LookupAccountSid 比使用 SecurityIdentifier.Translate() 更好。我在拥有 60k+ 用户的多林/域环境中工作。
    • Translate方法返回组的pre-Windows 2000名称。除了 PInvoke,您还可以使用 FindByIdentity 方法 oonSystem.DirectoryServices.AccountManagement.GroupPrincipal
    【解决方案3】:

    System.DirectoryServices.AccountManagement.UserPrincipal 类 (msdn link) 有一个静态函数 FindByIdentity 将 SID 转换为用户对象。它应该能够在本地机器或 LDAP/Active Directory 服务器上工作。我只对活动目录使用过。

    这是我在 IIS 中使用的示例:

    // Set the search context to a specific domain in active directory
    var searchContext = new PrincipalContext(ContextType.Domain, "YOURDOMAIN", "OU=SomeOU,DC=YourCompany,DC=com");
    // get the currently logged in user from IIS
    MembershipUser aspUser = Membership.GetUser();
    // get the SID of the user (stored in the SecurityIdentifier class)
    var sid = aspUser.ProviderUserKey as System.Security.Principal.SecurityIdentifier;
    // get the ActiveDirectory user object using the SID (sid.Value returns the SID in string form)
    var adUser = UserPrincipal.FindByIdentity(searchContext, IdentityType.Sid, sid.Value);
    // do stuff to user, look up group membership, etc.
    

    【讨论】:

    • 这是查询外域的正确方法。不需要 P/Invoke。
    【解决方案4】:

    在 C# 中,获取用户 SID 并通过以下方式将其分配给字符串变量:

    string strUser = System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString();
    

    您将需要使用字符串,因为解析为用户名的能力支持字符串。也就是说,使用var varUser会导致命名空间错误。

    string strUserName = new System.Security.Principal.SecurityIdentifier(strUser).Translate(typeof(System.Security.Principal.NTAccount)).ToString();
    

    【讨论】:

    • 这导致了问题=“此工作站与主域之间的信任关系失败”
    • @user1034912 您进行了哪些故障排除?您是否在管理员模式下运行 VS?你检查 MSDN 是否有这个错误:support.microsoft.com/en-us/help/2771040/…
    • 算了,我通过重新加入域解决了这个问题……很痛苦……但它奏效了!
    【解决方案5】:

    您还可以使用这样的代码获取特殊帐户的帐户名称,例如“Everyone”,无论用户的语言设置如何:

       SecurityIdentifier everyoneSid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
       string everyone = everyoneSid.Translate(typeof(System.Security.Principal.NTAccount)).ToString();
    

    【讨论】:

    • Tks。这对我来说是一个救生员:)
    【解决方案6】:

    哦,那么 LDAP 调用可能不起作用,因为您可能不在 Active Directory 环境中。如果是这种情况,那么您的每台机器都负责自己的身份存储。而且您的第一个代码示例无法通过网络运行,因为您正在执行代码的机器不知道如何解析仅在远程机器上才有意义的 SID。

    您确实应该检查您的计算机是否是 Active Directory 的一部分。您会在登录过程中知道这一点。或者您可以通过右键单击“我的电脑”来检查,选择“属性”,“计算机名称”选项卡,然后查看您的计算机是否属于域。

    【讨论】:

      【解决方案7】:

      太好了。我从这里抄录了一些 LookupAccountSid() 代码:

      http://www.pinvoke.net/default.aspx/advapi32.LookupAccountSid

      这很有效,但我必须自己提供主机名。在 UNC 路径的情况下,我可以只取它的第一个组件。当它是映射驱动器时,我使用此代码将路径转换为 ​​UNC 路径:

      http://www.wiredprairie.us/blog/index.php/archives/22

      它似乎有效,所以我将这样做,除非有人想出 UNC 路径的第一个组成部分不是主机名的情况......

      感谢大家的帮助。

      【讨论】:

        【解决方案8】:

        这是一个笨蛋。您在 Active Directory 环境中,对吗?只是检查:)

        无论如何,而不是与 sid.Value 绑定,

        string str = "LDAP://<SID=" + sid.Value + ">";
        

        我会尝试将 SID 的字节数组转换为 Octet String 并与之绑定。

        第 78 页上有一个很好的例子here。这会让你更接近。老实说,我以前没有尝试过与 SID 绑定。但是我已经成功绑定了用户的 GUID :)

        祝你好运,让我知道进展如何。

        【讨论】:

          【解决方案9】:

          获取当前域:

          System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain();
          

          从 ldap 获取目录项和域名:

          DirectoryEntry de = new DirectoryEntry(string.Format("LDAP://{0}", domain));
          

          从 ActiveDirectoryMembershipProvider ActiveDirectoryMembershipUser 获取 sid:

          ActiveDirectoryMembershipUser user = (ActiveDirectoryMembershipUser)Membership.GetUser();
          var sid = (SecurityIdentifier)user.ProviderUserKey;
          

          从 SecurityIdentifier 中获取用户名:

          (NTAccount)sid.Translate(typeof(NTAccount));
          

          使用域目录条目和用户名在活动目录上完成目录搜索:

          DirectorySearcher search = new DirectorySearcher(entry);
                  search.Filter = string.Format("(SAMAccountName={0})", username);
                  search.PropertiesToLoad.Add("Name");
                  search.PropertiesToLoad.Add("displayName");
                  search.PropertiesToLoad.Add("company");
                  search.PropertiesToLoad.Add("homePhone");
                  search.PropertiesToLoad.Add("mail");
                  search.PropertiesToLoad.Add("givenName");
                  search.PropertiesToLoad.Add("lastLogon");
                  search.PropertiesToLoad.Add("userPrincipalName");
                  search.PropertiesToLoad.Add("st");
                  search.PropertiesToLoad.Add("sn");
                  search.PropertiesToLoad.Add("telephoneNumber");
                  search.PropertiesToLoad.Add("postalCode");
                  SearchResult result = search.FindOne();
                  if (result != null)
                  {
                      foreach (string key in result.Properties.PropertyNames)
                      {
                          // Each property contains a collection of its own
                          // that may contain multiple values
                          foreach (Object propValue in result.Properties[key])
                          {
                              outputString += key + " = " + propValue + ".<br/>";
                          }
                      }
                  }
          

          根据您的活动目录中的数据,您将在输出中得到不同的响应。

          Here is a site that has all the user properties I needed:

          【讨论】:

            【解决方案10】:

            我很确定您将能够使用此处接受的答案:Determine the LocalSystem account name using C#

            基本上,您可以将 SecurityIdentifier 类的实例转换为 NTAccount 类型,从中可以获取用户名。在代码中:

            using System.Security.Principal;
            
            SecurityIdentifier sid = new SecurityIdentifier("S-1-5-18");
            NTAccount acct = (NTAccount)sid.Translate(typeof(NTAccount));
            Console.WriteLine(acct.Value);
            

            【讨论】:

            • 虽然这不是错误的,但它只是重复了发布者已经指出的代码在他的情况下不起作用。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-12-02
            • 1970-01-01
            • 1970-01-01
            • 2017-06-17
            • 2011-08-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多