ฉันจะรันการทดสอบ Internet Explorer Selenium ในฐานะผู้ใช้โดเมนเฉพาะได้อย่างไร

ฉันมีเว็บไซต์ ASP.NET MVC ที่ใช้ Windows Authentication เพื่อควบคุมการเข้าถึง ฉันต้องการให้มีการทดสอบซีลีเนียม specflow ที่ตรวจสอบว่าการกำหนดค่าถูกต้องโดยพยายามเยี่ยมชมไซต์ในฐานะผู้ใช้ที่ไม่ได้รับอนุญาต

เนื่องจากเราใช้บัญชีโดเมนเพื่อควบคุมการเข้าถึง จึงไม่มีหน้าจอเข้าสู่ระบบชื่อผู้ใช้/รหัสผ่าน ข้อมูลรับรองของผู้ใช้ปัจจุบันจะถูกส่งไปยังไซต์โดยอัตโนมัติโดยเบราว์เซอร์

ดังนั้นสำหรับการทดสอบซีลีเนียมของฉัน ฉันจำเป็นต้องเรียกใช้ Internet Explorer ในฐานะผู้ใช้เฉพาะได้

ฉันพบบทความจำนวนหนึ่งเกี่ยวกับการเลียนแบบ Windows และฉันสามารถสลับไปใช้ผู้ใช้ทดสอบของฉันได้ในระหว่างการทดสอบ (โดยใช้โค้ดจาก http://support.microsoft.com/kb/306158) อย่างไรก็ตามหากฉันสร้าง InternetExplorerDriver จากนั้นมันก็จะเริ่ม Internet Explorer ด้วยข้อมูลประจำตัวของฉันแทนที่จะเป็นของผู้ใช้ทดสอบ (แม้ว่าคำถามและคำตอบนี้จะแนะนำว่าควรใช้งานได้ https://sqa.stackexchange.com/questions/2277/using-selenium-webdriver-with-windows-authentication)

ฉันยังสามารถเริ่มกระบวนการ Internet Explorer ได้อย่างชัดเจนในฐานะผู้ใช้ทดสอบของฉัน แต่ฉันไม่เห็นวิธีผูก InternetExplorerDriver กับกระบวนการ Internet Explorer ที่ทำงานอยู่แล้ว ดังนั้นนี่อาจเป็นทางตัน

รหัสของฉันโดยทั่วไปนำมาจากหน้า MSDN ด้านบนอยู่ด้านล่าง ในดีบักเกอร์ ฉันเห็นว่า WindowsIdentity.GetCurrent().Name คือ "testUser" ในทุกขั้นตอนของการทดสอบ

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();
    }
}

}


comment
อาจต้องลอง: ใส่ driver อินสแตนซ์ของคุณก่อน impersonateValidUser(..)   -  person ddavison    schedule 20.01.2015
comment
ขอบคุณสำหรับแนวคิดนี้ แต่ฉันกลัวว่ามันจะแก้ไขอะไรไม่ได้   -  person Dan    schedule 20.01.2015
comment
อาจลองเริ่มต้นการทดสอบจากบรรทัดคำสั่งของ Windows ผ่านทาง runas /user:USER@DOMIAN path\to\mstest.exe ...   -  person Greg Burghardt    schedule 28.01.2015
comment
นั่นอาจจะได้ผล แต่คุณจะต้องเตะหนึ่งครั้งต่อผู้ใช้หนึ่งคน ตอนนี้ฉันได้หยุดตรวจสอบเรื่องนี้แล้ว เนื่องจากฉันมีการทดสอบที่จำเป็นเพียงสองครั้งเท่านั้น และฉันสามารถเรียกใช้การทดสอบด้วยตนเองได้ภายในไม่กี่นาที   -  person Dan    schedule 03.02.2015
comment
มีคนถามคำถามที่คล้ายกันในบริบทของ Java: stackoverflow.com/questions/18017883/ - แต่ยังคง 'ยังไม่ได้ตอบ'   -  person indra    schedule 18.02.2015


คำตอบ (8)


เรามีไคลเอ็นต์ระดับองค์กรจำนวนมากที่ใช้ Windows Authentication สำหรับแอปพลิเคชันที่เชื่อมต่อกับอินทราเน็ต และเรากำลังเริ่มดำเนินการทดสอบ Selenium จำนวนมากเพื่อยืนยัน การถดถอย ฯลฯ

เราได้นำโค้ดที่เป็นประโยชน์จากคำตอบของ Steven มาปรับโครงสร้างใหม่ให้เป็นคลาสที่นำมาใช้ใหม่ได้ ซึ่งคล้ายกับโพสต์ Impersonate อื่นๆ ที่ไม่ได้ผลสำหรับเรา เพราะเราต้องการให้การทดสอบทำงานทั้งภายในเครื่องในการพัฒนาและปรับใช้โดยเป็นส่วนหนึ่งของ Visual ขั้นตอนการเปิดตัวระบบทีมสตูดิโอ

วิธี uri ไม่ทำงานภายในเครื่องและไม่ได้เลียนแบบวิธีการโดยใช้วิธีดั้งเดิมของ Win32

อันนี้ใช้งานได้ดังนั้นนี่คือ

ตัวอย่างการทดสอบโดยใช้โค้ดของ Steven ที่ปรับโครงสร้างใหม่เป็นตัวช่วย

[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!");
        }
    }
}

ชั้นเรียนผู้ช่วย

// 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;
            }
        }
    }
}

บางทีนี่อาจช่วยคนอื่นที่มีปัญหาเดียวกันได้

person Rick Glos    schedule 01.06.2016
comment
ขอบคุณสำหรับชั้นเรียนนี้ มันช่วยให้รู้ว่าฉันต้องการทำอะไรจริงๆ ฉันมีปัญหากับข้อมูลการเข้าสู่ระบบและฉันได้โพสต์เกี่ยวกับเรื่องนี้ที่นี่: stackoverflow.com/questions/47687030/ คุณรู้ไหมว่าฉันจะผ่านพ้นสิ่งนี้/สิ่งที่ฉันทำผิดได้อย่างไร ฉันได้รับข้อผิดพลาดเดียวกันเมื่อใช้รหัสที่ไม่ได้แก้ไขของคุณด้วย - person Rescis; 08.12.2017
comment
ตรรกะของโค้ดนี้ชัดเจนมาก ประการแรก เริ่มกระบวนการไดรเวอร์ Chrome โดยใช้ตัวเลียนแบบ จากนั้นเชื่อมต่อกับไดรเวอร์ผ่าน Json Wire Protocol และ RemoteWebDriver เก่งมาก ทำได้ดีมาก! - person Richard; 03.07.2020

นี่คือความจริงที่เป็นไปได้ ฉันพบปัญหาที่แน่นอนที่คุณมี โดยพื้นฐานแล้ว นี่คือขั้นตอนที่คุณต้องทำ

  1. เปิดไดรเวอร์เบราว์เซอร์ด้วยตนเองโดยใช้ข้อมูลรับรองของผู้ใช้รายอื่นในเบื้องหลัง

    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. ใช้ Remote Web Driver เพื่อเชื่อมต่ออินสแตนซ์ไดรเวอร์เบราว์เซอร์ที่เปิดใช้งานด้วยตนเอง

    var remoteDriver = new RemoteWebDriver(Uri("http://localhost:5555"), DesiredCapabilities.InternetExplorer());
    
  3. อย่าลืมปิด/ออก/ยุติกระบวนการไดรเวอร์และอินสแตนซ์ของเบราว์เซอร์เมื่อดำเนินการเสร็จแล้ว

    // 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
ขั้นตอนที่ 1 ขาดวิธีที่คุณเริ่มกระบวนการด้วยตนเอง โดยเฉพาะออบเจ็กต์ info - person Rick Glos; 31.05.2016
comment
@RickGlos ฉันได้อัปเดตขั้นตอนที่ 1 เพื่อรวมวิธีที่ฉันรับวัตถุ info - person Steven; 31.05.2016
comment
ขอบคุณ ด้วยรายละเอียดเพิ่มเติมที่ชัดเจน ฉันจึงสามารถใช้งานสิ่งนี้ได้ แม้ว่าเราจะใช้งาน Chrome ก็ตาม แต่ดูเหมือนว่าจะง่ายพอๆ กับการเปลี่ยน driverPath ไปใช้ chromedriver กับ IEDriverServer นอกจากนี้เรายังจำเป็นต้องใช้สิ่งนี้ซ้ำแล้วซ้ำเล่า ดังนั้นฉันจะโพสต์คำตอบในไม่ช้าที่เราใช้อยู่ในขณะนี้ในหลายวิธีที่สร้างขึ้นจากคุณ ขอบคุณอีกครั้ง! - person Rick Glos; 01.06.2016

คำถามที่คล้ายกันนี้ลิงก์ไปยัง บทความสนับสนุนของ Microsoft โดยพื้นฐานแล้วคุณต้องการ

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

มีโค้ดเพิ่มเติมในบทความสนับสนุนเกี่ยวกับการแอบอ้างเป็นผู้ใช้รายใดรายหนึ่ง

person Scott Rickman    schedule 10.02.2015
comment
ฉันคิดว่านั่นคือรหัสที่ Dan ใช้อยู่แล้ว นั่นเป็นบทความ KB เดียวกับที่เขาเชื่อมโยงในคำถามของเขา - person Rup; 10.02.2015
comment
การเลียนแบบใช้งานได้ แต่ผู้ใช้ Internet Explorer ไม่เปลี่ยนแปลงสำหรับฉัน - person ColacX; 12.02.2015

คุณมีพีซีเก่าสองสามเครื่องหรือไม่? หรือความจุของเครื่องเสมือนบางเครื่อง?

หากเป็นเช่นนั้น ให้สร้างการตั้งค่า Selenium Grid และกำหนดค่าให้เข้าสู่ระบบอัตโนมัติในฐานะผู้ใช้โดเมนที่ต้องการ และอีกรายการหนึ่งเป็นผู้ใช้ที่ไม่ใช่โดเมน
http://code.google.com/p/selenium/wiki/Grid2

person Bigwave    schedule 19.02.2015

ฉันประสบปัญหาเดียวกันเมื่อฉันทำโปรเจ็กต์อัตโนมัติสำหรับแอปพลิเคชันบนเว็บซึ่งจำเป็นต้องมีการตรวจสอบสิทธิ์หน้าต่าง อย่างไรก็ตาม ฉันทำสิ่งนี้สำเร็จได้ด้วยการใช้ Firefox โดยทำตามขั้นตอนต่อไปนี้

การตั้งค่าไฟร์ฟ็อกซ์

  1. เปิดกล่องโต้ตอบการเรียกใช้ของระบบของคุณและประเภท 'firefox.exe -p' (ปิดเบราว์เซอร์ FIREFOX ของคุณก่อนเรียกใช้คำสั่งนี้) http://www.wikihow.com/Create-a-Firefox-Profile
  2. คลิกที่สร้างโปรไฟล์และตั้งชื่อตามที่ต้องการ
  3. เลือกโปรไฟล์ที่สร้างขึ้นและเริ่มเบราว์เซอร์และเปิดตัวจัดการส่วนเสริม (เครื่องมือ - ส่วนเสริม)
  4. ค้นหา 'AutoAuth' และติดตั้ง มันจะขอให้เริ่มต้นใหม่ ทำมัน
  5. เมื่อรีสตาร์ท FIREFOX แล้ว URL ที่เปิดอยู่จะขอให้คุณตรวจสอบสิทธิ์
  6. ป้อนชื่อผู้ใช้และรหัสผ่าน - ส่งมัน FIREFOX จะขอให้คุณจำรหัสผ่าน
  7. คลิกที่จำ และมันจะบันทึกรหัสผ่านในโปรไฟล์ FIREFOX
  8. คัดลอกโปรไฟล์ FIREFOX ที่สร้างขึ้นและบันทึกลงในโฟลเดอร์ที่ต้องการ
  9. ในการโทรสคริปต์ซีลีเนียมของคุณข้างต้นสร้างโปรไฟล์ด้วยไดรเวอร์ FIREFOX และส่ง URL เดียวกัน โดยจะไม่ขอกล่องโต้ตอบการตรวจสอบสิทธิ์

สิ่งนี้ทำงานได้สำเร็จมากในโครงการของฉัน

person Karim Narsindani    schedule 05.07.2015

เราใช้แนวทาง https://stackoverflow.com/a/31540010/3489693 สำหรับ IE และ Chrome เป็นเวลากว่า 2 ปี มันทำงานได้ดี

person mdementev    schedule 21.07.2015

ดูเหมือนว่าปัญหาที่คำถามพยายามหลีกเลี่ยงนั้นเกี่ยวข้องกับการเข้าสู่ระบบ NTLM อัตโนมัติ ดู การเข้าสู่ระบบอัตโนมัติของ Google Chrome และ NTLM โดยใช้การรับรองความถูกต้องของ Windows

วิธีแก้ปัญหาข้างต้นใช้ไม่ได้สำหรับฉันเนื่องจากการเข้าสู่ระบบอัตโนมัติจะตรวจสอบสิทธิ์กับผู้ใช้ใดๆ ในระบบของฉันได้สำเร็จ ดังนั้นจึงไม่สำคัญว่าฉันใช้ผู้ใช้รายใดในการแอบอ้างบุคคลอื่น

อย่างไรก็ตาม ฉันสังเกตเห็นว่าคุณสามารถเอาชนะการเข้าสู่ระบบอัตโนมัติได้ด้วยการแทนที่ localhost ด้วยชื่อโดเมนอื่น เช่น ที่อยู่ IP ในเครื่อง ไม่จำเป็นต้องแอบอ้าง :)

person bgh    schedule 23.06.2016

สิ่งนี้อาจ/อาจไม่ทำงาน

  • ลองเปิดเว็บไซต์ของคุณใน "CHROME"
  • กด F-12 ไปที่แท็บแอปพลิเคชัน -> คุกกี้ -> คลิกที่ลิงก์เว็บไซต์ของคุณ ทางด้านซ้ายมือ ให้มองหาสิ่งที่แสดงถึงรหัสเซสชันของคุณ อาจเป็น JSESSIONID หรือคล้ายกันที่แสดงถึงเซสชันของผู้ใช้ คัดลอกสิ่งนั้น
  • ตอนนี้เปิด Internet Explorer ของคุณ
  • กด F-12 และสร้าง JSESSIONID (หรือคีย์ที่คล้ายกัน) ด้วยตนเองโดยการรันคำสั่งนี้ในหน้าต่างคอนโซล

document.cookie = "JSESSIONID=your-session-id-from-chrome"

  • กดปุ่มเล่นเพื่อรันสคริปต์
  • รีเฟรชเบราว์เซอร์ของคุณ
person ATHER    schedule 20.02.2019