Bagaimana cara menjalankan tes Internet Explorer Selenium sebagai pengguna domain tertentu?

Saya memiliki situs web ASP.NET MVC yang menggunakan Otentikasi Windows untuk mengontrol akses. Saya ingin melakukan tes specflow Selenium yang memeriksa apakah konfigurasi sudah benar dengan mencoba mengunjungi situs sebagai pengguna yang tidak sah.

Karena kami menggunakan akun domain untuk mengontrol akses, tidak ada layar login nama pengguna/kata sandi. Kredensial pengguna saat ini secara otomatis diteruskan ke situs oleh browser.

Jadi untuk tes Selenium saya, saya harus bisa menjalankan Internet Explorer sebagai pengguna tertentu.

Saya telah menemukan sejumlah artikel tentang peniruan identitas Windows dan saya dapat beralih ke pengguna pengujian saya selama menjalankan pengujian (menggunakan kode dari http://support.microsoft.com/kb/306158). Namun jika saya kemudian membuat InternetExplorerDriver, ia memulai internet explorer dengan kredensial saya, bukan dengan pengguna uji (walaupun pertanyaan dan jawaban ini menyarankan bahwa itu harus berfungsi https://sqa.stackexchange.com/questions/2277/using-selenium-webdriver-with-windows-authentication).

Saya juga dapat secara eksplisit memulai proses Internet Explorer sebagai pengguna pengujian saya, tetapi saya tidak dapat melihat cara untuk mengikat InternetExplorerDriver ke proses Internet Explorer yang sudah berjalan, jadi ini mungkin jalan buntu.

Kode saya, pada dasarnya diambil dari halaman MSDN di atas ada di bawah. Di debugger saya dapat melihat bahwa WindowsIdentity.GetCurrent().Name adalah "testUser" di semua langkah pengujian.

namespace MyProject.Specs
{
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.IE;
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using TechTalk.SpecFlow;

[Binding]
public class AuthorisationSteps
{
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_PROVIDER_DEFAULT = 0;
    private static WindowsImpersonationContext impersonationContext;
    private static IWebDriver driver;

    [BeforeScenario]
    public static void impersonateUser()
    {
        if (!impersonateValidUser("testUser", "testDomain", "password"))
        {
            throw new Exception();
        }
        driver = new InternetExplorerDriver();
    }

    [AfterScenario]
    public static void cleanupUser()
    {
        undoImpersonation();
        driver.Quit();
    }

    [Given(@"I am an unauthorised user")]
    public void GivenIAmAnUnauthorisedUser()
    {
        var temp = WindowsIdentity.GetCurrent().Name;
    }

    [When(@"I go to the home page")]
    public void WhenIGoToTheHomePage()
    {
        var temp = WindowsIdentity.GetCurrent().Name;
        driver.Navigate().GoToUrl(BaseUrl);
    }

    [Then(@"I should see an error page")]
    public void ThenIShouldSeeAnErrorPage()
    {
        var temp = WindowsIdentity.GetCurrent().Name;
        Assert.That(driver.Title.Contains("Error"));
    }

    [DllImport("advapi32.dll")]
    public static extern int LogonUserA(String lpszUserName,
                                        String lpszDomain,
                                        String lpszPassword,
                                        int dwLogonType,
                                        int dwLogonProvider,
                                        ref IntPtr phToken);

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

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

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

    private static bool impersonateValidUser(String userName, String domain, String password)
    {
        WindowsIdentity tempWindowsIdentity;
        var token = IntPtr.Zero;
        var tokenDuplicate = IntPtr.Zero;

        if (RevertToSelf())
        {
            if (LogonUserA(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();
                    if (impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero)
        {
            CloseHandle(token);
        }
        if (tokenDuplicate != IntPtr.Zero)
        {
            CloseHandle(tokenDuplicate);
        }
        return false;
    }

    private static void undoImpersonation()
    {
        impersonationContext.Undo();
    }
}

}


person Dan    schedule 20.01.2015    source sumber
comment
mungkin sesuatu untuk dicoba: letakkan driver instantiasi Anda sebelum impersonateValidUser(..)   -  person ddavison    schedule 20.01.2015
comment
Terima kasih atas idenya, tapi saya khawatir itu tidak menyelesaikan masalah   -  person Dan    schedule 20.01.2015
comment
Mungkin coba memulai pengujian Anda dari baris perintah Windows melalui runas /user:USER@DOMIAN path\to\mstest.exe ...   -  person Greg Burghardt    schedule 28.01.2015
comment
Itu mungkin akan berhasil, tetapi Anda harus memulai satu kali proses per pengguna. Untuk saat ini saya sudah berhenti menyelidikinya karena saya hanya memiliki dua tes yang memerlukannya dan saya dapat menjalankannya secara manual dalam beberapa menit.   -  person Dan    schedule 03.02.2015
comment
Seseorang menanyakan pertanyaan serupa dalam konteks Java: stackoverflow.com/questions/18017883/ - tetapi tetap 'belum terjawab'.   -  person indra    schedule 18.02.2015


Jawaban (8)


Kami memiliki banyak klien perusahaan yang menggunakan Otentikasi Windows untuk aplikasi yang terhubung ke intranet dan kami mulai menjalankan banyak tes Selenium untuk konfirmasi, regresi, dll.

Kami telah mengambil kode bermanfaat dari jawaban Steven dan memfaktorkannya kembali menjadi kelas yang dapat digunakan kembali mirip dengan Impersonate postingan lain yang tidak berfungsi untuk kami karena kami ingin pengujian berfungsi baik secara lokal dalam pengembangan dan diterapkan sebagai bagian dari Visual Proses rilis Sistem Tim Studio.

Metode uri tidak berfungsi secara lokal dan juga tidak meniru metode yang menggunakan metode asli Win32.

Yang ini berhasil, jadi ini dia.

Contoh pengujian menggunakan kode Steven yang difaktorkan ulang menjadi pembantu

[TestMethod]
public void ThisApp_WhenAccessedByUnathorizedUser_ShouldDisallowAccess()
{
    string userName = "ThisAppNoAccess";
    string password = "123456";
    string domainName = Environment.MachineName;
    using (new Perkins.Impersonator(userName, domainName, password))
    {
        // - Use Remote Web Driver to hook up the browser driver instance launched manually.
        using (var driver = new RemoteWebDriver(new Uri("http://localhost:9515"), DesiredCapabilities.Chrome()))
        {
            var desiredUri = Helper.Combine(Helper.BaseURL, "/ThisApp/#/appGrid");
            TestContext.WriteLine("desiredUri: {0}", desiredUri);
            driver.Navigate().GoToUrl(desiredUri);
            Helper.WaitForAngular(driver);
            var noPermissionNotificationElement = driver.FindElementByXPath("//div[@ng-show='!vm.authorized']/div/div/div/p");
            var showsNoPermissionNotification = noPermissionNotificationElement.Text.Contains("You do not have permissions to view ThisApp.");
            Assert.AreEqual(true, showsNoPermissionNotification, "The text `You do not have permissions to view ThisApp.` is not being displayed!");
        }
    }
}

Kelas pembantu

// Idea from http://stackoverflow.com/a/34406336/16008
// - Launch the browser driver manually with other user's credentials in background
public class Perkins
{
    public class Impersonator : IDisposable
    {
        Process _driverProcess = null;
        string _driverPath = @"chromedriver.exe";
        /// <summary>
        /// Impersonates the specified user account by launching the selenium server under that account.  Connect to it via RemoteWebDriver and localhost on port 9515.
        /// </summary>
        /// <remarks>
        /// We may later want to enhance this by allowing for different ports, etc.
        /// </remarks>
        /// <param name="userName">Name of the user</param>
        /// <param name="domainName">Name of the domain or computer if using a local account.</param>
        /// <param name="password">The password</param>
        public Impersonator(string userName, string domainName, string password)
        {
            ProcessStartInfo processStartInfo = new ProcessStartInfo(_driverPath);
            processStartInfo.UserName = userName;
            System.Security.SecureString securePassword = new System.Security.SecureString();
            foreach (char c in password)
            {
                securePassword.AppendChar(c);
            }
            processStartInfo.Password = securePassword;
            processStartInfo.Domain = domainName; // this is important, mcollins was getting a 'stub received bad data' without it, even though rglos was not
            processStartInfo.UseShellExecute = false;
            processStartInfo.LoadUserProfile = true; // this seemed to be key, without this, I get Internal Server Error 500
            Thread startThread = new Thread(() =>
            {
                _driverProcess = Process.Start(processStartInfo);
                _driverProcess.WaitForExit();
            })
            { IsBackground = true };
            startThread.Start();
        }
        public void Dispose()
        {
            // - Remember to close/exit/terminate the driver process and browser instance when you are done.
            if (_driverProcess != null)
            {
                // Free managed resources
                if (!_driverProcess.HasExited)
                {
                    _driverProcess.CloseMainWindow();
                    _driverProcess.WaitForExit(5000);
                    // Kill the process if the process still alive after the wait
                    if (!_driverProcess.HasExited)
                    {
                        _driverProcess.Kill();
                    }
                    _driverProcess.Close();
                }
                _driverProcess.Dispose();
                _driverProcess = null;
            }
        }
    }
}

Mungkin ini akan membantu orang lain dengan masalah yang sama.

person Rick Glos    schedule 01.06.2016
comment
Terima kasih untuk kelas ini; itu sangat membantu dalam mencari tahu apa yang ingin saya lakukan. Saya mengalami masalah dengan informasi masuk, dan saya membuat postingan tentangnya di sini: stackoverflow.com/questions/47687030/ Tahukah Anda bagaimana saya bisa melewati ini/kesalahan apa yang saya lakukan? Saya juga mendapatkan kesalahan yang sama saat menggunakan kode Anda yang tidak dimodifikasi. - person Rescis; 08.12.2017
comment
Logika kode ini sangat jelas: pertama, mulai proses driver chrome menggunakan peniru, lalu sambungkan ke driver melalui Json Wire Protocol dan RemoteWebDriver. Sangat pintar, bagus sekali! - person Richard; 03.07.2020

Hal ini sebenarnya mungkin terjadi. Saya mengalami masalah persis seperti yang Anda alami. Pada dasarnya, berikut adalah langkah-langkah yang perlu Anda lakukan.

  1. Luncurkan driver browser secara manual dengan kredensial pengguna lain di latar belakang

    Process driverProcess;
    string driverPath; // The path to Selenium's IE driver.
    ProcessStartInfo info = new ProcessStartInfo(driverPath)
    {
        UserName = "UserName", // The user name.
        Password = new SecureString(), // The password for the user.
        UseShellExecute = false,
        LoadUserProfile = true,
        Arguments = "about:blank"
    };
    // Start the driver in background thread
    Thread startThread = new Thread(
        () => {
            try
            {
                driverProcess = Process.Start(info);
                driverProcess.WaitForExit();
            }
            catch
            {
                // Close the process.
            }
        })
    {
        IsBackground = true
    };
    startThread.Start();
    
  2. Gunakan Remote Web Driver untuk menghubungkan instance driver browser yang diluncurkan secara manual.

    var remoteDriver = new RemoteWebDriver(Uri("http://localhost:5555"), DesiredCapabilities.InternetExplorer());
    
  3. Ingatlah untuk menutup/keluar/mengakhiri proses driver dan browser setelah Anda selesai.

    // Close the process when done.
    if (driverProcess != null)
    {
        // Free managed resources
        if (!driverProcess.HasExited)
        {
            driverProcess.CloseMainWindow();
            driverProcess.WaitForExit(5000);
            // Kill the process if the process still alive after the wait
            if (!driverProcess.HasExited)
            {
                driverProcess.Kill();
            }
    
            driverProcess.Close();
        }
    
        driverProcess.Dispose();
        driverProcess = null;
    }
    
person Steven    schedule 12.11.2015
comment
Langkah 1 tidak mengetahui cara Anda memulai proses secara manual - khususnya, objek info. - person Rick Glos; 31.05.2016
comment
@RickGlos Saya memperbarui langkah 1 untuk memasukkan cara saya mendapatkan objek info. - person Steven; 31.05.2016
comment
terima kasih, dengan sedikit informasi tambahan yang jelas, saya bisa membuat ini berfungsi, meskipun kami melakukannya dengan Chrome, tampaknya semudah mengganti driverPath menjadi chromedriver vs IEDriverServer. Kami juga perlu menggunakan ini berulang kali, jadi saya akan segera memposting jawaban yang kami gunakan sekarang dalam banyak metode yang dikembangkan berdasarkan metode Anda. Terima kasih lagi! - person Rick Glos; 01.06.2016

pertanyaan serupa ini tertaut ke Artikel dukungan Microsoft. Intinya Anda membutuhkan

System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext = 
((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
IWebDriver webDriver = new InternetExplorerDriver();
// do your stuff here.
impersonationContext.Undo();

Ada kode tambahan di artikel dukungan tentang meniru identitas pengguna tertentu.

person Scott Rickman    schedule 10.02.2015
comment
Saya rasa itulah kode yang sudah digunakan Dan. Itu artikel KB yang sama yang dia tautkan dalam pertanyaannya. - person Rup; 10.02.2015
comment
peniruan identitas berfungsi tetapi pengguna internet explorer tidak berubah untuk saya. - person ColacX; 12.02.2015

Apakah Anda memiliki beberapa PC lama? Atau kapasitas untuk beberapa mesin virtual?

Jika ya, buat pengaturan Selenium Grid, dan konfigurasikan satu untuk login otomatis sebagai pengguna domain yang diinginkan dan satu lagi sebagai pengguna non-domain.
http://code.google.com/p/selenium/wiki/Grid2

person Bigwave    schedule 19.02.2015

Saya mengalami masalah yang sama ketika saya melakukan proyek otomasi untuk aplikasi berbasis web yang memerlukan otentikasi jendela. Namun, saya telah mencapainya dengan menggunakan firefox, berikut adalah langkah-langkah untuk mencapainya.

PENYIAPAN FIREFOX

  1. BUKA DIALOG JALANKAN SISTEM ANDA DAN KETIK 'firefox.exe -p' (TUTUP BROWSER FIREFOX ANDA SEBELUM MENJALANKAN PERINTAH INI) http://www.wikihow.com/Create-a-Firefox-Profile
  2. KLIK BUAT PROFIL DAN BERIKAN NAMA SEPERTI YANG DIPERLUKAN
  3. PILIH PROFIL YANG DIBUAT DAN MULAI BROWSER DAN BUKA MANAJER TAMBAHAN (ALAT - TAMBAHAN)
  4. CARI 'AutoAuth' DAN INSTAL. AKAN MEMINTA RESTART, LAKUKAN
  5. SETELAH FIREFOX DIMULAI KEMBALI, URL TERBUKA AKAN MEMINTA AUTHENTIKASI ANDA
  6. MASUKKAN NAMA PENGGUNA DAN PASSWORD - KIRIMKAN, FIREFOX AKAN MEMINTA ANDA UNTUK MENGINGAT PASSWORD
  7. KLIK INGAT DAN AKAN SIMPAN PASSWORD DI PROFIL FIREFOX
  8. SALIN PROFIL FIREFOX YANG DIBUAT DAN SIMPAN KE FOLDER YANG DIPERLUKAN
  9. DALAM SKRIP SELENIUM ANDA, PANGGILAN DI ATAS PROFIL YANG DIBUAT DENGAN DRIVER FIREFOX DAN LULUSKAN URL YANG SAMA, TIDAK AKAN MEMINTA DIALOG OTENTIKASI

Ini bekerja dengan sangat sukses di proyek saya.

person Karim Narsindani    schedule 05.07.2015

Kami menggunakan pendekatan https://stackoverflow.com/a/31540010/3489693 untuk IE dan Chrome selama 2 tahun. Ini berfungsi dengan baik

person mdementev    schedule 21.07.2015

Jadi sepertinya masalah yang ingin dihindari oleh pertanyaan ini ada hubungannya dengan Login Otomatis NTLM. Lihat Login Otomatis Google Chrome dan NTLM Menggunakan Autentikasi Windows

Solusi di atas tidak berhasil bagi saya karena login otomatis akan berhasil mengautentikasi dengan pengguna mana pun di sistem saya, jadi tidak masalah pengguna mana yang saya gunakan untuk meniru identitas.

Namun, saya perhatikan Anda bisa mengakali auto-login dengan mengganti localhost dengan nama domain lain, misalnya alamat IP lokal. Tidak perlu peniruan identitas :)

person bgh    schedule 23.06.2016

Ini mungkin/mungkin tidak berhasil.

  • Coba luncurkan situs Anda di "CHROME".
  • Tekan F-12, buka Tab Aplikasi -> Cookies -> Klik tautan situs Anda. di sisi kiri cari sesuatu yang mewakili id ​​sesi Anda, mungkin JSESSIONID atau serupa yang mewakili sesi pengguna, salin itu.
  • Sekarang buka Internet Explorer Anda,
  • tekan F-12 dan buat JSESSIONID (atau kunci serupa) secara manual dengan menjalankan perintah ini di jendela konsol

document.cookie = "JSESSIONID=id-sesi-Anda-dari-chrome"

  • tekan tombol putar untuk menjalankan skrip
  • Segarkan browser Anda
person ATHER    schedule 20.02.2019