【问题标题】:Are there better (easier) ways to get a specific domain's SID?是否有更好(更简单)的方法来获取特定域 SID?
【发布时间】:2011-10-11 12:34:04
【问题描述】:

我被指派修改 WinForms 应用程序,以检查登录用户是否属于特定域。 到目前为止,这是我想出的:

byte[] domainSid;

var directoryContext =
    new DirectoryContext(DirectoryContextType.Domain, "domain.se");

using (var domain = Domain.GetDomain(directoryContext))
using (var directoryEntry = domain.GetDirectoryEntry())
    domainSid = (byte[])directoryEntry.Properties["objectSid"].Value;

var sid = new SecurityIdentifier(domainSid, 0);
bool validUser = UserPrincipal.Current.Sid.IsEqualDomainSid(sid);

有没有更好/更简单的方法来做到这一点? 对我来说,似乎可以使用 PrincipalContext 或 System.Security.Principal 中的其他类以某种方式访问​​ domainSid。

我考虑过使用硬编码的 SID 字符串,但我不知道这有多“正确”。

【问题讨论】:

  • 请注意,在 Windows 的本地化版本中,管理员的名称可能不同。例如,在俄语 Windows 中没有“管理员”用户,而是“Администратор”,因此硬编码管理员的名称不是一个好主意。
  • 仅供参考:对于当前用户的域,您可以使用:WindowsIdentity.GetCurrent().User.AccountDomainSid
  • 注意:对于那些使用 PowerShell 的人:(Get-AdDomain -Identity 'myDomain').DomainSID 有效。该 cmdlet 是 ActiveDirectory 模块的一部分。 Get-AdDomain cmdlet 返回Microsoft.ActiveDirectory.Management.ADDomain 类型的对象。遗憾的是,该类没有任何特殊的 .net 选项来按希望将名称解析为 SID。

标签: c# .net-4.0 active-directory


【解决方案1】:

在我看来,你正在做的事情是最好的选择。硬编码字符串绝对不是一个好主意。

【讨论】:

    【解决方案2】:

    每个域都有一个内置帐户 domainName\administrator,因此您可以使用此名称创建一个帐户,将其转换为 SecurityIdentifier 并读取 AccountDomainSid 属性。

    这种方式的一个例子是:

    public static class SecurityEx
    {
        public static SecurityIdentifier DomainSId
        {
            get
            {               
                var administratorAcount = new NTAccount(GetDomainName(), "administrator");
                var administratorSId = (SecurityIdentifier) administratorAcount.Translate(typeof (SecurityIdentifier));
                return administratorSId.AccountDomainSid;
            }
        }
    
        internal static string GetDomainName()
        {
            //could be other way to get the domain name through Environment.UserDomainName etc...
            return IPGlobalProperties.GetIPGlobalProperties().DomainName;
        }
    }
    

    您还可以通过 WMI 或 Lsa 找到其他解决方案来实现相同的结果。这对我来说似乎是最优雅的方式。

    【讨论】:

    • 并非所有域都有“管理员”帐户。默认的“管理员”可以通过组策略重命名为其他名称。实际上,出于安全原因,建议重命名它。
    【解决方案3】:

    以下是接受答案的稍微不同的方法:

    C#

    using System.Security.Principal; 
    using System.DirectoryServices;  
    
    //...
    
    void Main()
    {
        var sidHelp = new SecurityIdentifierHelper();
        Console.WriteLine(sidHelp.GetDomainSecurityIdentifier("MyDomain"));
    }
    
    //...
    
    public class SecurityIdentifierHelper
    {
        const int OffsetBinaryFormStartingIndex = 0; // just to avoid magic numbers; though this is still a bit magic.  Documentation says "The byte offset to use as the starting index in binaryForm", whatever that means.
                                                     // https://referencesource.microsoft.com/#mscorlib/system/security/principal/sid.cs,d06a8d5ee0c7a26d,references
                                                     // https://docs.microsoft.com/en-us/dotnet/api/system.security.principal.securityidentifier.-ctor?view=netframework-4.8#System_Security_Principal_SecurityIdentifier__ctor_System_Byte___System_Int32_
        public SecurityIdentifier GetDomainSecurityIdentifier(string domain)
        {
            var ldapPath = $"LDAP://{domain}";
            var domainAdsi = new DirectoryEntry(ldapPath);
            var domainSidByteArray = (byte[])domainAdsi.Properties["objectSid"].Value;
            return new SecurityIdentifier(domainSidByteArray, OffsetBinaryFormStartingIndex);
        }
    }
    

    C# 一个班轮

    Console.WriteLine(new System.Security.Principal.SecurityIdentifier(((byte[])new System.DirectoryServices.DirectoryEntry("LDAP://MyDomain").Properties["objectSid"].Value), 0));
    

    PowerShell:

    [string]$myDomain = 'MyDomain' # or 'MyDomain.example.com'
    [string]$ldapPath = 'LDAP://{0}' -f $MyDomain
    [adsi]$domainAdsi = [adsi]$ldapPath
    [System.Security.Principal.SecurityIdentifier]::new($domainAdsi.Value, 0)
    

    PowerShell 1 衬里:

    [System.Security.Principal.SecurityIdentifier]::new(([ADSI]('LDAP://MyDomain')).Value, 0)
    

    【讨论】:

    • ([ADSI]('LDAP://MyDomain)).Value 不适合我;相反,请尝试([ADSI]('LDAP://MyDomain)).objectSid.Value 获取字节
    • ...而 OffsetBinaryFromStartingIndex 是给定字节数组中开始读取 SID 的位置——如果字节数组 则为 0(或开头)SID,但构造函数还允许传递一个包含 SID的字节数组,如果你知道它在哪里开始
    【解决方案4】:

    要获得域的 sid,只需要这个简单的查询:

    public static SecurityIdentifier GetDomainSid(string domainName)
    {
        byte[] domainSidBytes = ((byte[])new System.DirectoryServices.DirectoryEntry($"LDAP://{ domainName }").Properties["objectSid"].Value);
        SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(domainSidBytes, 0);
        return sid;
    }
    

    注意:当域名无法解析或用户无权查询Active Directory时,此方法会抛出异常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-05
      • 1970-01-01
      • 2019-10-05
      • 1970-01-01
      • 2021-03-24
      相关资源
      最近更新 更多