2012-03-28 64 views
9

我正在使用代碼模擬用戶帳戶以訪問文件共享。模仿Windows用戶

public class Impersonator : 
    IDisposable 
{ 
    #region Public methods. 
    // ------------------------------------------------------------------ 

    /// <summary> 
    /// Constructor. Starts the impersonation with the given credentials. 
    /// Please note that the account that instantiates the Impersonator class 
    /// needs to have the 'Act as part of operating system' privilege set. 
    /// </summary> 
    /// <param name="userName">The name of the user to act as.</param> 
    /// <param name="domainName">The domain name of the user to act as.</param> 
    /// <param name="password">The password of the user to act as.</param> 
    public Impersonator(
     string userName, 
     string domainName, 
     string password) 
    { 
     ImpersonateValidUser(userName, domainName, password); 
    } 

    // ------------------------------------------------------------------ 
    #endregion 

    #region IDisposable member. 
    // ------------------------------------------------------------------ 

    public void Dispose() 
    { 
     UndoImpersonation(); 
    } 

    // ------------------------------------------------------------------ 
    #endregion 

    #region P/Invoke. 
    // ------------------------------------------------------------------ 

    [DllImport("advapi32.dll", SetLastError=true)] 
    private static extern int LogonUser(
     string lpszUserName, 
     string lpszDomain, 
     string lpszPassword, 
     int dwLogonType, 
     int dwLogonProvider, 
     ref IntPtr phToken); 

    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] 
    private static extern int DuplicateToken(
     IntPtr hToken, 
     int impersonationLevel, 
     ref IntPtr hNewToken); 

    [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] 
    private static extern bool RevertToSelf(); 

    [DllImport("kernel32.dll", CharSet=CharSet.Auto)] 
    private static extern bool CloseHandle(
     IntPtr handle); 

    private const int LOGON32_LOGON_INTERACTIVE = 2; 
    private const int LOGON32_PROVIDER_DEFAULT = 0; 

    // ------------------------------------------------------------------ 
    #endregion 

    #region Private member. 
    // ------------------------------------------------------------------ 

    /// <summary> 
    /// Does the actual impersonation. 
    /// </summary> 
    /// <param name="userName">The name of the user to act as.</param> 
    /// <param name="domainName">The domain name of the user to act as.</param> 
    /// <param name="password">The password of the user to act as.</param> 
    private void ImpersonateValidUser(
     string userName, 
     string domain, 
     string password) 
    { 
     WindowsIdentity tempWindowsIdentity = null; 
     IntPtr token = IntPtr.Zero; 
     IntPtr tokenDuplicate = IntPtr.Zero; 

     try 
     { 
      if (RevertToSelf()) 
      { 
       if (LogonUser(
        userName, 
        domain, 
        password, 
        LOGON32_LOGON_INTERACTIVE, 
        LOGON32_PROVIDER_DEFAULT, 
        ref token) != 0) 
       { 
        if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) 
        { 
         tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); 
         impersonationContext = tempWindowsIdentity.Impersonate(); 
        } 
        else 
        { 
         throw new Win32Exception(Marshal.GetLastWin32Error()); 
        } 
       } 
       else 
       { 
        throw new Win32Exception(Marshal.GetLastWin32Error()); 
       } 
      } 
      else 
      { 
       throw new Win32Exception(Marshal.GetLastWin32Error()); 
      } 
     } 
     finally 
     { 
      if (token!= IntPtr.Zero) 
      { 
       CloseHandle(token); 
      } 
      if (tokenDuplicate!=IntPtr.Zero) 
      { 
       CloseHandle(tokenDuplicate); 
      } 
     } 
    } 

    /// <summary> 
    /// Reverts the impersonation. 
    /// </summary> 
    private void UndoImpersonation() 
    { 
     if (impersonationContext!=null) 
     { 
      impersonationContext.Undo(); 
     } 
    } 

    private WindowsImpersonationContext impersonationContext = null; 

    // ------------------------------------------------------------------ 
    #endregion 
} 

然後使用:

using (new Impersonator("username", "domain", "password")) 
     { 
      Process.Start("explorer.exe", @"/root,\\server01-Prod\abc"); 
     } 

我得到錯誤 「訪問被拒絕」。

該用戶相當有權訪問此共享。我可以映射驅動器,使用「淨使用」,但此代碼不起作用。現在我認爲這是代碼。有人看到任何東西嗎?有沒有更好的方法來做到這一點?

+0

這是從哪裏來的?如果這是一個託管在IIS上的應用程序,那麼默認的IIS用戶可能沒有模仿 – oleksii 2012-03-28 14:55:14

+0

的權利。 IIS中的託管的Web應用程序 – 2012-03-28 16:20:44

+0

我記得我有一個類似的問題,請嘗試使用管理員用戶在簡單的控制檯應用程序中運行此代碼。我實際上並不確定你能從IIS上運行的Web應用程序執行此操作。這與ASP.NET用戶的權限有關(據我記憶) – oleksii 2012-03-28 17:24:35

回答

5

試試這個:

[DllImport("advapi32.dll", SetLastError = true)] 
    public static extern bool LogonUser(
      string lpszUsername, 
      string lpszDomain, 
      string lpszPassword, 
      int dwLogonType, 
      int dwLogonProvider, 
      out IntPtr phToken); 

用法:

IntPtr userToken = IntPtr.Zero; 

bool success = External.LogonUser(
    "john.doe", 
    "domain.com", 
    "MyPassword", 
    (int) AdvApi32Utility.LogonType.LOGON32_LOGON_INTERACTIVE, //2 
    (int) AdvApi32Utility.LogonProvider.LOGON32_PROVIDER_DEFAULT, //0 
    out userToken); 

if (!success) 
{ 
    throw new SecurityException("Logon user failed"); 
} 

using (WindowsIdentity.Impersonate(userToken)) 
{ 
Process.Start("explorer.exe", @"/root,\\server01-Prod\abc"); 
} 
+0

我正在使用同一段代碼,看到我無法解釋的這種行爲:有時LogonUser會返回退出代碼0和Win32Exceptionis:「System.ComponentModel.Win32Exception:目錄名稱無效」。但之後10分鐘後重新運行相同的功能。你碰巧知道爲什麼?如有必要,我可以爲您提供更多的上下文。 – 2013-05-20 23:23:29

1

而不是使用您的Impersonator類,當你調用Process.Start並傳遞一個包含用戶名一ProcessStartInfo情況下會發生什麼,密碼和您想要運行該過程的域?

也許,如果可行,那麼你的Impersonator類應該創建一個ProcessStartInfo實例並使用它創建新的進程(將其封裝在類本身內)。

var psi = new ProcessStartInfo("explorer.exe", @"/root,\\server01-Prod\abc"); 
psi.Domain = domain; 
psi.UserName = username; 
psi.Password = password; 
psi.WorkingDirectory = workingDir; 

Process.Start(psi); 

而且,每MSDN docs ...

設置域名,用戶名,並在 ProcessStartInfo對象的密碼屬性是用於啓動 過程中使用用戶憑據推薦的做法。

您還應該在啓動具有不同用戶信用的進程時設置工作目錄。

+0

我沒有收到錯誤消息,但我期待瀏覽器窗口打開。 psi.Domain =「域」; psi.UserName =「usera」; psi.Password = pwd; psi.FileName =「explorer.exe」; psi.Arguments = @「/ root,\\ server01-pv \ abc」; psi.ErrorDialog = true; psi.UseShellExecute = false;的Process.Start(PSI); – 2012-03-28 16:19:24

+0

當你設置UseShellExecute = true時會發生什麼? – 2012-03-28 16:31:02

+0

另外,如果你設置'ErrorDialog = true',我認爲你必須設置'UseShellExecute = true'。並且在ProcessStartInfo實例上設置'WorkingDirectory'。 – 2012-03-28 16:31:41

2

如果我理解正確的話,你的意圖是運行在模擬上下文的過程。

來自CreateProcess的文檔(由Process.Start使用)說: 如果調用進程正在模擬另一個用戶,則新進程將使用令牌作爲調用進程,而不是模擬令牌。要在由模擬令牌表示的用戶的安全上下文中運行新進程,請使用CreateProcessAsUser或CreateProcessWithLogonW函數。

所以,你使用了錯誤的API做這件事。