在C#中搜索Windows用户SID

时间:2010-05-04 21:51:22

标签: c# windows authentication sid

上下文

首先是上下文 - 我试图解决的问题如下。

[编辑]问题中的应用程序是针对.NET 3.5 SP1构建的。

我们的一位客户询问我们需要多长时间才能完善我们的某个应用程序。此应用程序当前以用户名/密码组合的形式提供基本用户身份验证。此客户希望员工能够使用运行应用程序时当前登录的任何Windows用户帐户的详细信息登录。

如果我不告诉他们,这不是一个交易破坏者 - 但是客户可能愿意支付开发成本以将此功能添加到应用程序中。值得研究。

根据我的搜索情况,如果更改了这些细节,似乎存储针对Domain \ Username的用户登录详细信息会有问题。但Windows用户SID根本不应该改变。我的印象是最好通过SID记录Windows用户 - 如果我错了,请随意解除我的想法。

我一直在使用一些Windows API调用。在C#中,抓取当前用户的SID很容易。我已经可以使用任何用户的SID并使用LookupAccountSid处理它以获取用户名和域以供显示。对于感兴趣的人,我的代码就在这篇文章的最后。

然而,这只是冰山一角。以下两个问题完全不符合我的经验。我不仅不知道如何实现它们 - 我甚至不知道如何实现它们,或者在各种系统上存在哪些缺陷。

非常感谢任何让我自己瞄准正确方向的帮助。

问题1)

如果未授予该用户对该应用程序的访问权限,则在运行时获取本地用户是没有意义的。我们需要在应用程序的“管理员控制台”中添加一个新部分,用于添加Windows用户(或组)并为这些用户分配应用内应用权限。

类似于“添加Windows用户登录”按钮,会弹出一个弹出窗口,允许用户在网络(而不仅仅是本地计算机)上搜索可用的Windows用户帐户要添加到可用应用程序登录列表中。

如果.NET或Windows中已经有一个组件可以为我做这个,那会让我很开心。

问题2)

我还想知道如何获取给定的Windows用户SID并针对给定的Windows用户组(可能来自数据库)进行检查。我不知道如何开始使用这个,但我希望它比上面的问题更容易。

感兴趣的

[STAThread]
static void Main(string[] args)
{
    MessageBox.Show(WindowsUserManager.GetAccountNameFromSID(WindowsIdentity.GetCurrent().User.Value));
    MessageBox.Show(WindowsUserManager.GetAccountNameFromSID("S-1-5-21-57989841-842925246-1957994488-1003"));
}

public static class WindowsUserManager
{
    public static string GetAccountNameFromSID(string SID)
    {
        try
        {
            StringBuilder name = new StringBuilder();
            uint cchName = (uint)name.Capacity;
            StringBuilder referencedDomainName = new StringBuilder();
            uint cchReferencedDomainName = (uint)referencedDomainName.Capacity;
            WindowsUserManager.SID_NAME_USE sidUse;

            int err = (int)ESystemError.ERROR_SUCCESS;
            if (!WindowsUserManager.LookupAccountSid(null, SID, name, ref cchName, referencedDomainName, ref cchReferencedDomainName, out sidUse))
            {
                err = Marshal.GetLastWin32Error();
                if (err == (int)ESystemError.ERROR_INSUFFICIENT_BUFFER)
                {
                    name.EnsureCapacity((int)cchName);
                    referencedDomainName.EnsureCapacity((int)cchReferencedDomainName);

                    err = WindowsUserManager.LookupAccountSid(null, SID, name, ref cchName, referencedDomainName, ref cchReferencedDomainName, out sidUse) ?
                        (int)ESystemError.ERROR_SUCCESS :
                        Marshal.GetLastWin32Error();
                }
            }

            if (err != (int)ESystemError.ERROR_SUCCESS)
                throw new ApplicationException(String.Format("Could not retrieve acount name from SID. {0}", SystemExceptionManager.GetDescription(err)));

            return String.Format(@"{0}\{1}", referencedDomainName.ToString(), name.ToString());
        }
        catch (Exception ex)
        {
            if (ex is ApplicationException)
                throw ex;

            throw new ApplicationException("Could not retrieve acount name from SID", ex);
        }
    }

    private enum SID_NAME_USE
    {
        SidTypeUser = 1,
        SidTypeGroup,
        SidTypeDomain,
        SidTypeAlias,
        SidTypeWellKnownGroup,
        SidTypeDeletedAccount,
        SidTypeInvalid,
        SidTypeUnknown,
        SidTypeComputer
    }

    [DllImport("advapi32.dll", EntryPoint = "GetLengthSid", CharSet = CharSet.Auto)]
    private static extern int GetLengthSid(IntPtr pSID);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool ConvertStringSidToSid(
                string StringSid,
                out IntPtr ptrSid);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool LookupAccountSid(
      string lpSystemName,
      [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
      StringBuilder lpName,
      ref uint cchName,
      StringBuilder ReferencedDomainName,
      ref uint cchReferencedDomainName,
      out SID_NAME_USE peUse);

    private static bool LookupAccountSid(
        string lpSystemName,
        string stringSid,
        StringBuilder lpName,
        ref uint cchName,
        StringBuilder ReferencedDomainName,
        ref uint cchReferencedDomainName,
        out SID_NAME_USE peUse)
    {
        byte[] SID = null;
        IntPtr SID_ptr = IntPtr.Zero;
        try
        {
            WindowsUserManager.ConvertStringSidToSid(stringSid, out SID_ptr);

            int err = SID_ptr == IntPtr.Zero ? Marshal.GetLastWin32Error() : (int)ESystemError.ERROR_SUCCESS;

            if (SID_ptr == IntPtr.Zero ||
                err != (int)ESystemError.ERROR_SUCCESS)
                throw new ApplicationException(String.Format("'{0}' could not be converted to a SID byte array. {1}", stringSid, SystemExceptionManager.GetDescription(err)));

            int size = (int)GetLengthSid(SID_ptr);
            SID = new byte[size];

            Marshal.Copy(SID_ptr, SID, 0, size);
        }
        catch (Exception ex)
        {
            if (ex is ApplicationException)
                throw ex;

            throw new ApplicationException(String.Format("'{0}' could not be converted to a SID byte array. {1}.", stringSid, ex.Message), ex);
        }
        finally
        {
            // Always want to release the SID_ptr (if it exists) to avoid memory leaks.
            if (SID_ptr != IntPtr.Zero)
                Marshal.FreeHGlobal(SID_ptr);
        }

        return WindowsUserManager.LookupAccountSid(lpSystemName, SID, lpName, ref cchName, ReferencedDomainName, ref cchReferencedDomainName, out peUse);
    }
}

1 个答案:

答案 0 :(得分:3)

如果你使用的是3.5版本的框架,那么你 想要查看System.DirectoryServices.AccountManagement。我之前使用它来提供AD帐户的查找,并且处理比编写自己的类要简单得多。它也将解决你的#2问题。我手边没有代码,但是如果你需要它我可以随时查找。