2010-04-20 61 views
8

我們在我們的應用程序中使用NTLM身份驗證來確定用戶是否可以執行某些操作。我們使用他們當前Windows登錄的IPrincipal(在WinForms應用程序中),調用IsInRole來檢查特定的組成員身份。在Windows 7上調用IPrincipal.IsInRole

要檢查用戶的計算機上的本地管理員,我們使用:

AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); 
... 
bool allowed = Thread.CurrentPrincipal.IsInRole(@"Builtin\Administrators") 

這工作,如果當前用戶是Administrator用戶,或者是說是Builtin\Administrators組的成員另一個用戶。

在我們對Windows 7的測試中,我們發現這不再按預期工作。 Administrator用戶仍然可以正常工作,但作爲Builtin\Administrators組成員的任何其他用戶都會爲IsInRole呼叫返回false。

什麼可能導致這種差異?我有一種直覺,認爲默認設置已經改變了某處(gpedit可能),但找不到任何看起來像罪魁禍首的東西。

+0

問完這個問題之後,我有一些手動檢查組成員並在Vista機器上覆制行爲的代碼。然後,我就意識到UAC是個問題 - 在這裏運行Vista的大多數機器都關閉了UAC。 應用程序檢查從另一個源定義的組的成員資格,因此它可能會因應用程序是否需要以管理員身份運行而不同 - 清單文件太過苛刻。我認爲改變應用程序中的默認組可能是最簡單的解決方案。 – adrianbanks 2010-04-21 11:25:21

回答

9

問題是,Windows安全(又名「UAC」)正在阻礙你。有特殊的管理員角色處理,你的用戶將不會有這些角色,直到他被提升。管理角色在某種意義上是「虛擬的」:存在但不可用於權限檢查,或者甚至不能用於(容易)測試存在。見注於: http://msdn.microsoft.com/en-us/library/46ks97y7.aspx

這裏有一系列的談論這個問題,與示例代碼,做了必要的變通辦法:

我解決了類似的問題在ASP.NET應用程序中,通過構建我自己的UAC提示並使用名稱&密碼來調用Win32 Logon API。您可能很幸運能夠使用.NET桌面應用程序,在這種情況下,您可以使用常規提升請求。

這裏有一些C#代碼來檢查管理權限而不提升。

public const UInt32 TOKEN_DUPLICATE = 0x0002; 
    public const UInt32 TOKEN_IMPERSONATE = 0x0004; 
    public const UInt32 TOKEN_QUERY = 0x0008; 

    public enum TOKEN_ELEVATION_TYPE 
    { 
     TokenElevationTypeDefault = 1, 
     TokenElevationTypeFull, 
     TokenElevationTypeLimited 
    } 

    public enum TOKEN_INFORMATION_CLASS 
    { 
     TokenUser = 1, 
     TokenGroups, 
     TokenPrivileges, 
     TokenOwner, 
     TokenPrimaryGroup, 
     TokenDefaultDacl, 
     TokenSource, 
     TokenType, 
     TokenImpersonationLevel, 
     TokenStatistics, 
     TokenRestrictedSids, 
     TokenSessionId, 
     TokenGroupsAndPrivileges, 
     TokenSessionReference, 
     TokenSandBoxInert, 
     TokenAuditPolicy, 
     TokenOrigin, 
     TokenElevationType, 
     TokenLinkedToken, 
     TokenElevation, 
     TokenHasRestrictions, 
     TokenAccessInformation, 
     TokenVirtualizationAllowed, 
     TokenVirtualizationEnabled, 
     TokenIntegrityLevel, 
     TokenUIAccess, 
     TokenMandatoryPolicy, 
     TokenLogonSid, 
     MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum 
    } 

    public enum SECURITY_IMPERSONATION_LEVEL 
    { 
     SecurityAnonymous, 
     SecurityIdentification, 
     SecurityImpersonation, 
     SecurityDelegation 
    } 


    public static bool IsAdmin() 
    { 
     var identity = WindowsIdentity.GetCurrent(); 
     return (null != identity && new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator)); 
    } 

    /// <summary> 
    /// The function checks whether the primary access token of the process belongs 
    /// to user account that is a member of the local Administrators group, even if 
    /// it currently is not elevated. 
    /// </summary> 
    /// <returns> 
    /// Returns true if the primary access token of the process belongs to user 
    /// account that is a member of the local Administrators group. Returns false 
    /// if the token does not. 
    /// </returns> 
    public static bool CanBeAdmin() 
    { 
     bool fInAdminGroup = false; 
     IntPtr hToken = IntPtr.Zero; 
     IntPtr hTokenToCheck = IntPtr.Zero; 
     IntPtr pElevationType = IntPtr.Zero; 
     IntPtr pLinkedToken = IntPtr.Zero; 
     int cbSize = 0; 

     if (IsAdmin()) 
      return true; 

     try 
     { 
      // Check the token for this user 
      hToken = WindowsIdentity.GetCurrent().Token; 

      // Determine whether system is running Windows Vista or later operating 
      // systems (major version >= 6) because they support linked tokens, but 
      // previous versions (major version < 6) do not. 
      if (Environment.OSVersion.Version.Major >= 6) 
      { 
       // Running Windows Vista or later (major version >= 6). 
       // Determine token type: limited, elevated, or default. 

       // Allocate a buffer for the elevation type information. 
       cbSize = sizeof(TOKEN_ELEVATION_TYPE); 
       pElevationType = Marshal.AllocHGlobal(cbSize); 
       if (pElevationType == IntPtr.Zero) 
       { 
        throw new Win32Exception(Marshal.GetLastWin32Error()); 
       } 

       // Retrieve token elevation type information. 
       if (!GetTokenInformation(hToken, 
        TOKEN_INFORMATION_CLASS.TokenElevationType, pElevationType, cbSize, out cbSize)) 
       { 
        throw new Win32Exception(Marshal.GetLastWin32Error()); 
       } 

       // Marshal the TOKEN_ELEVATION_TYPE enum from native to .NET. 
       TOKEN_ELEVATION_TYPE elevType = (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(pElevationType); 

       // If limited, get the linked elevated token for further check. 
       if (elevType == TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited) 
       { 
        // Allocate a buffer for the linked token. 
        cbSize = IntPtr.Size; 
        pLinkedToken = Marshal.AllocHGlobal(cbSize); 
        if (pLinkedToken == IntPtr.Zero) 
        { 
         throw new Win32Exception(Marshal.GetLastWin32Error()); 
        } 

        // Get the linked token. 
        if (!GetTokenInformation(hToken, 
         TOKEN_INFORMATION_CLASS.TokenLinkedToken, pLinkedToken, 
         cbSize, out cbSize)) 
        { 
         throw new Win32Exception(Marshal.GetLastWin32Error()); 
        } 

        // Marshal the linked token value from native to .NET. 
        hTokenToCheck = Marshal.ReadIntPtr(pLinkedToken); 
       } 
      } 

      // CheckTokenMembership requires an impersonation token. If we just got 
      // a linked token, it already is an impersonation token. If we did not 
      // get a linked token, duplicate the original into an impersonation 
      // token for CheckTokenMembership. 
      if (hTokenToCheck == IntPtr.Zero) 
      { 
       if (!DuplicateToken(hToken, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, ref hTokenToCheck)) 
       { 
        throw new Win32Exception(Marshal.GetLastWin32Error()); 
       } 
      } 

      // Check if the token to be checked contains admin SID. 
      WindowsIdentity id = new WindowsIdentity(hTokenToCheck); 
      WindowsPrincipal principal = new WindowsPrincipal(id); 
      fInAdminGroup = principal.IsInRole(WindowsBuiltInRole.Administrator); 
     } 
     catch 
     { 
      return false; 
     } 
     finally 
     { 
      // Centralized cleanup for all allocated resources. 
      if (pElevationType != IntPtr.Zero) 
      { 
       Marshal.FreeHGlobal(pElevationType); 
       pElevationType = IntPtr.Zero; 
      } 
      if (pLinkedToken != IntPtr.Zero) 
      { 
       Marshal.FreeHGlobal(pLinkedToken); 
       pLinkedToken = IntPtr.Zero; 
      } 
     } 

     return fInAdminGroup; 
    } 

它改編自我在網上找到的一篇文章,對不起,失去了歸屬。

+0

更好的選擇可能是重新考慮您的設計,以便它不需要用戶成爲管理員。也許你使用某種Windows服務具有管理權限。問題是,使用ASP應用程序時,您向Web用戶公開了管理權限,如果代碼中存在允許人們執行arbibary代碼的缺陷,則這可能會很糟糕。 – 2010-04-20 18:49:47

+0

沒有辦法避免它。但是你是對的,將需要管理員權限的代碼包裝在「提升/撤銷」中是一個好主意,所以沒有曝光。 (這是我做的) – 2010-04-20 23:02:37

+0

非常感謝你,史蒂夫! – 2010-11-25 14:13:52

1

您的應用程序未被提升。在正常情況下,UAC將剝離用戶的「管理員權限」。如果應用程序只能由管理員使用,請添加一個使其升級的清單,以便他們可以保留其管理權限。如果它可以被任何一個使用,最好的辦法是分成兩部分,一部分是升降清單,另一部分是沒有的部分,然後從用盾牌裝飾的按鈕或菜單項啓動升高的部分,以便用戶不會點擊如果他們不是管理員。 (在較舊的操作系統上,屏蔽該按鈕的消息將被忽略。)在「UAC」,「分區」和「shellexecute」上搜索將會很有幫助。

7

這爲我工作 - 所有我需要的是檢查,如果方案已在管理角色已經啓動:

public static bool IsAdminRole() 
    { 
     AppDomain domain = Thread.GetDomain(); 

     domain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); 
     WindowsPrincipal principle = (WindowsPrincipal)Thread.CurrentPrincipal; 
     return principle.IsInRole(WindowsBuiltInRole.Administrator); 
    } 

希望有人認爲,使用!

邁克

+0

很好的回答,使用它! – 2015-05-20 15:52:15

3

我發現這裏計算器上的另一篇文章從而​​能夠解決這個法子。 我改編成下面的方法。 使用Windows 7時,管理員返回true,非管理員返回true,以「以管理員身份運行」時返回非管理員。 看起來這隻適用於.Net 3.5和XP SP2及更高版本,基於對MSDN的PrincipleContext類的初步介紹。

private static bool IsUserAdmin() 
{ 
    bool isAdmin = false; 

    WindowsIdentity wi = WindowsIdentity.GetCurrent(); 
    WindowsPrincipal wp = new WindowsPrincipal(wi); 
    isAdmin = wp.IsInRole(WindowsBuiltInRole.Administrator); 

    Console.WriteLine(isAdmin); // False for Windows 7 even if user is admin 

    //found the code below at [http://stackoverflow.com/questions/1089046/in-net-c-test-if-user-is-an-administrative-user][1] 

    // Add reference to System.DirectoryServices.AccountManagement (Add Referemce -> .Net) 
    // Add using System.DirectoryServices.AccountManagement; 

    if (!isAdmin) //PrincipleContext takes a couple seconds, so I don't use it if not necessary 
    { 
     using (PrincipalContext pc = new PrincipalContext(ContextType.Machine, null)) 
     { 
      UserPrincipal up = UserPrincipal.Current; 
      GroupPrincipal gp = GroupPrincipal.FindByIdentity(pc, "Administrators"); 
      if (up.IsMemberOf(gp)) 
      { 
       isAdmin = true; 
      } 
     } 
    } 
    Console.WriteLine(isAdmin); // True for Windows 7 if user is admin 


    return isAdmin; 
}