การเข้าถึง 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.