4

语境

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

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

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

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

根据我的搜寻,如果更改这些详细信息,似乎存储用户登录详细信息对域\用户名将是有问题的。但是 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);
    }
}
4

1 回答 1

3

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

于 2010-05-04T22:01:07.733 回答