以前老覺得,用C#寫身份切換(用不同身份存取資源)程式得借重API,有一堆細節要處理,麻煩得很。

這回發了狠,一口氣把複雜工作通通裝在一個Class裡,呼叫時只要傳入帳號、密碼和網域,取回WindowsImpersonationContext,一切搞定。

就叫它【C#變身術懶人包】吧!

排版顯示純文字
//REF: http://support.microsoft.com/kb/319615
public class ImpersonateHelper
{
    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LogonUser(string lpszUsername, 
        string lpszDomain, string lpszPassword, int dwLogonType, 
        int dwLogonProvider, ref IntPtr phToken);
 
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, 
        SetLastError = true)]
    private static extern bool CloseHandle(IntPtr handle);
 
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, 
        SetLastError = true)]
    public extern static bool DuplicateToken(IntPtr existingTokenHandle, 
        int SECURITY_IMPERSONATION_LEVEL, ref IntPtr duplicateTokenHandle);
 
    // logon types
    const int LOGON32_LOGON_INTERACTIVE = 2;
    const int LOGON32_LOGON_NETWORK = 3;
    const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
 
    // logon providers
    const int LOGON32_PROVIDER_DEFAULT = 0;
    const int LOGON32_PROVIDER_WINNT50 = 3;
    const int LOGON32_PROVIDER_WINNT40 = 2;
    const int LOGON32_PROVIDER_WINNT35 = 1;
 
    public static IntPtr GetDupToken(string userName, string password, 
        string domain)
    {
        IntPtr token = IntPtr.Zero;
        IntPtr dupToken = IntPtr.Zero;
        bool isSuccess = LogonUser(
                            userName,
                            domain,
                            password,
                            LOGON32_LOGON_NEW_CREDENTIALS,
                            LOGON32_PROVIDER_DEFAULT,
                            ref token);
        if (!isSuccess)
            throw new ApplicationException(
                "Failed to LogonUser, Code = " + 
                Marshal.GetLastWin32Error());
        isSuccess = DuplicateToken(token, 2, ref dupToken);
        if (!isSuccess)
            throw new ApplicationException(
                "Failed to DuplicateToken, Code = " + 
                Marshal.GetLastWin32Error());
        return dupToken;
    }
 
    public static WindowsImpersonationContext 
        GetImpersonationContext(
        string userName, string password, string domainName)
    {
        return new WindowsIdentity(
    ImpersonateHelper.GetDupToken(userName, password, domainName)
            ).Impersonate();
    }
 
}
static void test()
{
    Dictionary<string, string> latestList = new Dictionary<string, string>();
    using (WindowsImpersonationContext wic =
        ImpersonateHelper.GetImpersonationContext("username", "pwd",
        "domainOrRemoteServerIp"))
    {
        foreach (string f in
            Directory.GetFiles(@"\\remoteServer\Folder", "*.txt"))
            Console.WriteLine(f);
        wic.Undo();
    }
}

Comments

# by 過路客

domainOrRemoveServerIp=DomainOrRemoteServerIP ?

# by Jeffrey

to 過路客, 謝謝指正,打錯字了

# by Xman

請問版大,您是否有使用Case tool記錄開發系統的相關文件? 如果有的話,是用那種工具呢?謝謝。

# by Jeffrey

to Xman, 其實我不算軟體工程方法論的信徒(還有點像叛徒),純粹是愛Coding而已, 說明文件不講究格式規範,以清楚陳述為主(跟寫Blog一樣爾偶還不忘搞笑),工作上的文件只用VSS保留改版歷程而已。但我的程式碼註解量蠻多的,積陰德順便拯救自己的健忘。

# by Dogg04

請問為什麼我沒辦法使用 (WindowsImpersonationContext wic = ImpersonateHelper.GetImpersonationContext("administrator", "easyuse", "192.168.66.128") 登入呢?? 會出現"語彙基元不可以為零" ...是我哪邊參數給錯了嗎?

# by Dogg04

版大..剛剛的問題解決了...但是還有另一個問題~~ 為什麼我就算密碼或是登入帳號亂輸入~~"isSuccess" 參數仍然是 "true" 呢??

# by Kim

Dear 黑大 剛測試程式碼,若是要存取跨網域,要使用下面的設定 LOGON32_LOGON_INTERACTIVE 環境只能Run在xp,2003...

Post a comment