การเข้าถึง HttpListener ถูกปฏิเสธ


180

ฉันกำลังเขียนเซิร์ฟเวอร์ HTTP ใน C #

เมื่อฉันพยายามที่จะใช้ฟังก์ชั่นที่HttpListener.Start()ฉันได้รับการHttpListenerExceptionพูด

"ปฏิเสธการเข้าใช้".

เมื่อฉันรันแอพในโหมดผู้ดูแลระบบใน windows 7 มันใช้งานได้ดี

ฉันสามารถทำให้มันทำงานโดยไม่มีโหมดผู้ดูแลระบบได้หรือไม่ ถ้าใช่เป็นอย่างไร หากไม่ใช่ฉันจะทำให้แอพเปลี่ยนเป็นโหมดผู้ดูแลระบบหลังจากเริ่มทำงานได้อย่างไร

using System;
using System.Net;

namespace ConsoleApplication1
{
    class Program
    {
        private HttpListener httpListener = null;

        static void Main(string[] args)
        {
            Program p = new Program();
            p.Server();
        }

        public void Server()
        {
            this.httpListener = new HttpListener();

            if (httpListener.IsListening)
                throw new InvalidOperationException("Server is currently running.");

            httpListener.Prefixes.Clear();
            httpListener.Prefixes.Add("http://*:4444/");

            try
            {
                httpListener.Start(); //Throws Exception
            }
            catch (HttpListenerException ex)
            {
                if (ex.Message.Contains("Access is denied"))
                {
                    return;
                }
                else
                {
                    throw;
                }
            }
        }
    }
}

หากมีคนต้องการหลีกเลี่ยงข้อผิดพลาดนั้นเขาสามารถลองเขียนมันด้วย TcpListener มันไม่จำเป็นต้องสิทธิ์ผู้ดูแลระบบ
— Vlad

ฉันเผชิญกับปัญหาเดียวกันใน Visual Studio 2008 + Windows 7 มันผลิตข้อผิดพลาด 'การเข้าถึงถูกปฏิเสธ' เพื่อตอบโต้การแก้ปัญหานี้คือการเรียกใช้ Visual Studio 2008 ในโหมดผู้ดูแลระบบ
— Mark Khor

คำตอบ:


307

ใช่คุณสามารถเรียกใช้ HttpListener ในโหมดที่ไม่ใช่ผู้ดูแลระบบ สิ่งที่คุณต้องทำคือให้สิทธิ์กับ URL นั้น ๆ เช่น

netsh http add urlacl url=http://+:80/MyUri user=DOMAIN\user

เอกสารที่นี่


48
สิ่งนี้มีประโยชน์ แต่เพื่อความสมบูรณ์ URL ที่ระบุในบรรทัดของรหัสนี้: httpListener.Prefixes.Add("http://*:4444/");จะต้องตรงกับURL ในnetshคำสั่ง ตัวอย่างเช่นฉันมีhttpListener.Prefixes.Add("http://127.0.0.1:80/");และnetshคำสั่งเดียวกับที่คุณมีและ HttpListenerException จะยังคงถูกโยนทิ้งไป ฉันต้องการเปลี่ยนhttpListener.Prefixes.Add("http://+:80/");ขอบคุณสำหรับความช่วยเหลือของคุณ @Darrel Miller เพราะคุณได้รับเส้นทางที่ถูกต้องในการหาสิ่งนี้!
— psyklopz

10
และอย่าลืมสแลชต่อท้ายหาก "MyUri" ว่างเปล่ามิฉะนั้นคุณจะได้รับThe parameter is incorrectข้อผิดพลาด ตัวอย่าง:url=http://+:80/
— Igor Brejc

14
มีวิธีใดในการทำเช่นนี้สำหรับผู้ใช้ที่ไม่ใช่ผู้ดูแลระบบหรือแม้กระทั่งเพื่อhttp://localhost:80/? ฉันมีแอปพลิเคชันเดสก์ท็อปที่ต้องการรับคำขอหนึ่งรายการใน URL ดังกล่าวและเป็นที่น่าเสียดายที่ผู้ดูแลระบบต้องติดตั้งลงบนเดสก์ท็อป 50 เครื่องเพียงเพื่อจุดประสงค์นี้
— John Saunders

5
ชัดเจนว่าไม่. นอกจากนี้ยังไม่มีการกล่าวถึงสิทธิในการยกระดับหรือสิทธิ์ระดับผู้ดูแลในหน้าเอกสาร ดังนั้นจึงง่ายที่จะสมมติว่าสิ่งนี้จะทำหน้าที่เป็น "ชาญฉลาด" TCPListener เมื่อในความเป็นจริงมีเหตุผลสำคัญที่จะไม่ใช้มัน - แต่นี่ไม่ใช่เอกสาร
— David Ford

4
การเรียกใช้ netsh ต้องใช้ผู้ดูแลระบบ (
— superfly71

27

ฉันสามารถทำให้มันทำงานโดยไม่มีโหมดผู้ดูแลระบบได้หรือไม่ ถ้าใช่เป็นอย่างไร หากไม่ใช่ฉันจะทำให้แอพเปลี่ยนเป็นโหมดผู้ดูแลระบบหลังจากเริ่มทำงานได้อย่างไร

คุณทำไม่ได้มันต้องเริ่มด้วยการยกระดับสิทธิ์ คุณสามารถเริ่มต้นใหม่ได้ด้วยrunasคำกริยาซึ่งจะแจ้งให้ผู้ใช้สลับไปที่โหมดผู้ดูแลระบบ

static void RestartAsAdmin()
{
    var startInfo = new ProcessStartInfo("yourApp.exe") { Verb = "runas" };
    Process.Start(startInfo);
    Environment.Exit(0);
}

แก้ไข: จริง ๆ แล้วนั่นไม่จริง HttpListener สามารถทำงานได้โดยไม่มีสิทธิ์ยกระดับ แต่คุณต้องให้สิทธิ์สำหรับ URL ที่คุณต้องการฟัง ดูคำตอบของ Darrel Millerสำหรับรายละเอียด


คุณช่วยอธิบายได้ไหมว่าทำไมฉันต้องเริ่มต้นด้วยสิทธิ์ที่ยกระดับ
— Randall Flagg

คุณต้องเพิ่มบรรทัดนี้ 'startInfo.UseShellExecute = false;' ก่อนหน้า 'Process.Start (startInfo);'
— Randall Flagg

@ มาตรฐาน: เป็นวิธีที่ Windows ใช้งานได้ ... กระบวนการไม่สามารถเปลี่ยนเป็นโหมดผู้ดูแลระบบในขณะที่ทำงานอยู่ เกี่ยวกับ UseShellExecute: ขึ้นอยู่กับสิ่งที่คุณกำลังดำเนินการ ฉันทดสอบโค้ดของฉันด้วย "notepad.exe" มันใช้งานได้ดีโดยไม่มี UseShellExecute = false
— Thomas Levesque

ขอบคุณ เกี่ยวกับ UseShellExecute: ฉันพยายามเรียกใช้รหัสที่ฉันโพสต์ ปัญหาอีกข้อหนึ่งก็คือมันถามฉันหนึ่งครั้งว่าฉันต้องการเรียกใช้ในฐานะผู้ดูแลระบบหรือเวลาอื่นหลังจากนั้นไม่ถาม ฉันรีสตาร์ท Debugged เพื่อให้แน่ใจว่าจะไปที่นั่นและไม่มีอะไร ข้อเสนอแนะใด ๆ
— Randall Flagg

1
@Thomas ไม่มีปัญหาในการเรียกใช้ HttpListener ในโหมดที่ไม่ใช่ผู้ดูแลระบบ
— Darrel Miller

23

หากคุณใช้http://localhost:80/เป็นคำนำหน้าคุณสามารถฟังคำขอ http โดยไม่จำเป็นต้องมีสิทธิ์ระดับผู้ดูแลระบบ


2
คุณสามารถโพสต์โค้ดตัวอย่างได้ไหม ฉันเพิ่งลองhttp://localhost:80/และได้รับ "Access Denied"
— John Saunders

2
ขออภัยดูเหมือนจะไม่ทำงาน โปรดตรวจสอบว่าคุณใช้งานในฐานะผู้ดูแลระบบหรือไม่ซึ่งไม่ใช่กรณีปกติ
— Jonno

ถ้ามันใช้งานได้ฉันจะถือว่าเป็นบั๊กของ windows ไม่ว่าคุณจะคิดอย่างไรเกี่ยวกับ windows ฉันถือว่านี่เป็นระดับพื้นฐานที่ดีมากมีโอกาสมากที่พวกเขาจะไม่พลาดการจัดการอย่างถูกต้อง
— hoijui

26
ดังนั้นมากกว่า 2 ปีต่อมาสิ่งนี้ใช้ได้กับฉันใน Windows Server 2008 R2 ด้วย. NET Framework 4.5 httpListener.Prefixes.Add("http://*:4444/");แสดงAccess Deniedข้อผิดพลาด แต่httpListener.Prefixes.Add("http://localhost:4444/");ทำงานได้โดยไม่มีปัญหา ดูเหมือนว่า Microsoft จะถูกแยกออกlocalhostจากข้อ จำกัด เหล่านี้ ที่น่าสนใจhttpListener.Prefixes.Add("http://127.0.0.1:4444/");ยังคงแสดงข้อผิดพลาดการปฏิเสธการเข้าถึงดังนั้นสิ่งเดียวที่ทำงานคือlocalhost:{any port}
— Tony

1
@Tony ขอบคุณความคิดเห็นของคุณจริง ๆ แล้วมีค่ามากที่สุดสำหรับฉัน ฉันมี "127.0.0.1" ในการกำหนดค่าหลายอย่าง QA รายงานว่ามันไม่ทำงานบน Windows 8.1 แต่อยู่ใน Windows 10 ได้ดี (และฉันทดสอบด้วยตัวเองใน Win10) ดูเหมือนว่า Microsoft ทุกคนได้รับอนุญาตให้ใช้ 127.0.0.1 ใน Win10 แต่ไม่ใช่ 8.1 (และอาจเป็นรุ่นก่อนหน้านี้) แต่ก็เพียงพอแล้ว localhost ก็ใช้ได้ ขอบคุณ
— Adam Plocher

20

ไวยากรณ์ผิดสำหรับฉันคุณต้องใส่เครื่องหมายคำพูด:

netsh http add urlacl url="http://+:4200/" user=everyone

ไม่เช่นนั้นฉันได้รับ "พารามิเตอร์ไม่ถูกต้อง"


4
"พารามิเตอร์ไม่ถูกต้อง" นอกจากนี้ยังสามารถส่งคืนได้หากคุณไม่ได้ระบุการติดตาม/ใน URL
— LostSalad

13

ในกรณีที่คุณต้องการใช้การตั้งค่าสถานะ "ผู้ใช้ = ทุกคน" คุณต้องปรับเป็นภาษาของระบบ ในภาษาอังกฤษมันเป็นดังกล่าว:

netsh http add urlacl url=http://+:80/ user=Everyone

ในภาษาเยอรมันมันจะเป็น:

netsh http add urlacl url=http://+:80/ user=Jeder

1
ในระบบภาษาสเปนให้ทำ: user = Todos
— Rodrigo Rodrigues

นอกจากนี้ฉันต้องใส่ URL ในเครื่องหมายคำพูดตามที่กล่าวไว้ในคำตอบอื่น
— Carsten Führmann

10

เป็นทางเลือกที่ไม่ต้องการการยกระดับหรือ netsh คุณสามารถใช้ TcpListener ได้เช่นกัน

ต่อไปนี้เป็นข้อความที่ตัดตอนมาจากตัวอย่างนี้: https://github.com/googlesamples/oauth-apps-for-windows/tree/master/OAuthDesktopApp

// Generates state and PKCE values.
string state = randomDataBase64url(32);
string code_verifier = randomDataBase64url(32);
string code_challenge = base64urlencodeNoPadding(sha256(code_verifier));
const string code_challenge_method = "S256";

// Creates a redirect URI using an available port on the loopback address.
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);
output("redirect URI: " + redirectURI);

// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope=openid%20profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
    authorizationEndpoint,
    System.Uri.EscapeDataString(redirectURI),
    clientID,
    state,
    code_challenge,
    code_challenge_method);

// Opens request in the browser.
System.Diagnostics.Process.Start(authorizationRequest);

// Waits for the OAuth authorization response.
var client = await listener.AcceptTcpClientAsync();

// Read response.
var response = ReadString(client);

// Brings this app back to the foreground.
this.Activate();

// Sends an HTTP response to the browser.
WriteStringAsync(client, "<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please close this window and return to the app.</body></html>").ContinueWith(t =>
{
    client.Dispose();
    listener.Stop();

    Console.WriteLine("HTTP server stopped.");
});

// TODO: Check the response here to get the authorization code and verify the code challenge

วิธีการอ่านและเขียนเป็น:

private string ReadString(TcpClient client)
{
    var readBuffer = new byte[client.ReceiveBufferSize];
    string fullServerReply = null;

    using (var inStream = new MemoryStream())
    {
        var stream = client.GetStream();

        while (stream.DataAvailable)
        {
            var numberOfBytesRead = stream.Read(readBuffer, 0, readBuffer.Length);
            if (numberOfBytesRead <= 0)
                break;

            inStream.Write(readBuffer, 0, numberOfBytesRead);
        }

        fullServerReply = Encoding.UTF8.GetString(inStream.ToArray());
    }

    return fullServerReply;
}

private Task WriteStringAsync(TcpClient client, string str)
{
    return Task.Run(() =>
    {
        using (var writer = new StreamWriter(client.GetStream(), new UTF8Encoding(false)))
        {
            writer.Write("HTTP/1.0 200 OK");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Type: text/html; charset=UTF-8");
            writer.Write(Environment.NewLine);
            writer.Write("Content-Length: " + str.Length);
            writer.Write(Environment.NewLine);
            writer.Write(Environment.NewLine);
            writer.Write(str);
        }
    });
}

สิ่งนี้มีประโยชน์จริง ๆ เนื่องจากเรามีปัญหา HttpListener เมื่อใช้งาน IBrowser สำหรับไลบรารี IdentityModel.OidcClient ดังนั้นกรณีการใช้ที่คล้ายกันมากกับข้างต้น
— แม็กกี้

1
เป็นที่น่าสังเกตว่ารหัสข้างต้นมีข้อบกพร่องอยู่ในนั้น ก่อนอื่นการใช้ StreamWriter กับ UTF8 ทำให้เกิดปัญหาในเบราว์เซอร์บางตัวฉันเดาว่าเนื่องจาก BOM เป็นเอาต์พุตหรืออะไรทำนองนั้น ฉันเปลี่ยนเป็น ASCII และทั้งหมดเป็นอย่างดี ฉันยังเพิ่มรหัสบางส่วนเพื่อรอให้ข้อมูลพร้อมอ่านบนสตรีมเนื่องจากบางครั้งไม่มีข้อมูลกลับมา
— mackie

ขอบคุณ @mackie - ส่วนนั้นนำมาจากตัวอย่างจริงของ Microsoft โดยตรง ฉันเดาว่าพวกเขาไม่ได้ทดสอบอย่างถูกต้อง หากคุณสามารถแก้ไขคำตอบของฉันไปข้างหน้าและแก้ไขวิธีการ - มิฉะนั้นอาจส่งการเปลี่ยนแปลงและฉันสามารถปรับปรุงได้
— Michael Olsen

2
แทนที่จะใช้ ASCII เราควรใช้ตัวสร้างต่อไปนี้new UTF8Encoding(false)ซึ่งจะปิดใช้งานการเปล่ง BOM ฉันเปลี่ยนสิ่งนี้แล้วในคำตอบ
— เซบาสเตียน

ฉันชอบวิธีนี้มากกว่าคำตอบที่ยอมรับ การเล่นเน็ตดูเหมือนว่าแฮ็ค นี่เป็นวิธีแก้ปัญหาทางโปรแกรมที่เป็นของแข็ง ขอบคุณ!
— จิม Gomes

7

คุณสามารถเริ่มต้นแอปพลิเคชันของคุณในฐานะผู้ดูแลระบบถ้าคุณเพิ่ม Application Manifest ในโครงการของคุณ

เพียงเพิ่มรายการใหม่ในโครงการของคุณและเลือก "Application Manifest File" เปลี่ยน<requestedExecutionLevel>องค์ประกอบเป็น:

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

7

โดยค่าเริ่มต้น Windows จะกำหนดคำนำหน้าต่อไปนี้ที่ทุกคนสามารถใช้ได้: http: // +: 80 / Temporary_Listen_Addresses /

ดังนั้นคุณสามารถลงทะเบียนHttpListenerผ่าน:

Prefixes.Add("http://+:80/Temporary_Listen_Addresses/" + Guid.NewGuid().ToString("D") + "/";

บางครั้งทำให้เกิดปัญหากับซอฟต์แวร์เช่น Skype ซึ่งจะพยายามใช้พอร์ต 80 เป็นค่าเริ่มต้น


1
Skype เป็นผู้ร้าย ฉันเปลี่ยนพอร์ตไม่ใช่ 80 และใช้งานได้
— GoTo

5
httpListener.Prefixes.Add("http://*:4444/");

คุณใช้ "*" เพื่อให้คุณดำเนินการตาม cmd ในฐานะผู้ดูแลระบบ

netsh http add urlacl url=http://*:4444/ user=username

ไม่มีประโยชน์ + ต้องใช้ * เนื่องจากคุณระบุ *: 4444 ~

https://msdn.microsoft.com/en-us/library/system.net.httplistener.aspx


นี่มันสำหรับฉัน มันใช้ได้ผลกับฉัน.Add("http://+:9000/")
— John Gietzen

2

ฉันยังประสบปัญหาที่คล้ายกันหากคุณจอง url ไว้แล้วคุณต้องลบ url ก่อนเพื่อให้ทำงานในโหมดที่ไม่ใช่ผู้ดูแลระบบมิฉะนั้นจะล้มเหลวเมื่อ Access is Denied error error

netsh http delete urlacl url=http://+:80
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.