Truy cập httpListener bị từ chối


180

Tôi đang viết một máy chủ HTTP bằng C #.

Khi tôi cố gắng thực hiện chức năng, HttpListener.Start()tôi nhận được một HttpListenerExceptioncâu nói

"Truy cập bị từ chối".

Khi tôi chạy ứng dụng ở chế độ quản trị viên trong windows 7, nó hoạt động tốt.

Tôi có thể làm cho nó chạy mà không có chế độ quản trị viên? nếu có thì thế nào? Nếu không, làm cách nào để ứng dụng thay đổi chế độ quản trị sau khi bắt đầu chạy?

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

Nếu ai đó muốn tránh lỗi đó, anh ta có thể thử viết nó bằng TcpListener. Nó không yêu cầu đặc quyền của quản trị viên
Vlad

Tôi gặp vấn đề tương tự, trong Visual Studio 2008 + Windows 7, nó tạo ra lỗi 'Truy cập bị từ chối', để khắc phục sự cố này là chạy Visual Studio 2008 trong Chế độ quản trị
Mark Khor

Câu trả lời:


307

Có, bạn có thể chạy HttpListener trong chế độ không phải quản trị viên. Tất cả những gì bạn cần làm là cấp quyền cho URL cụ thể. ví dụ

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

Tài liệu ở đây .


48
Điều này rất hữu ích, nhưng để đầy đủ, URL được chỉ định trong dòng mã này: httpListener.Prefixes.Add("http://*:4444/");phải khớp CHÍNH XÁC với một trong netshlệnh. Ví dụ, tôi đã có httpListener.Prefixes.Add("http://127.0.0.1:80/");và cùng một netshlệnh bạn có, và HTTPListenerException vẫn sẽ bị ném. Tôi cần thay đổi httpListener.Prefixes.Add("http://+:80/");Cảm ơn sự giúp đỡ của bạn @Darrel Miller, bởi vì bạn đã đưa tôi đi đúng hướng để tìm ra điều này!
psyklopz

10
Và đừng quên dấu gạch chéo nếu "MyUri" trống, nếu không bạn sẽ gặp The parameter is incorrectlỗi. Ví dụ:url=http://+:80/
Igor Brejc

14
Có cách nào để làm điều này cho một người dùng không phải là quản trị viên, ngay cả đối với http://localhost:80/? Tôi có một ứng dụng máy tính để bàn cần nhận một yêu cầu trên một URL như vậy và có vẻ xấu hổ khi yêu cầu quản trị viên cài đặt nó trên 50 máy tính để bàn, chỉ cho một mục đích này.
John Saunders

5
Rõ ràng là không. Cũng không có đề cập đến độ cao hoặc đặc quyền quản trị được yêu cầu trong trang tài liệu. Vì vậy, thật dễ dàng để giả định rằng điều này sẽ hoạt động như một TCPListener "thông minh hơn", trong khi thực tế có lý do CHÍNH không sử dụng nó - nhưng điều này chưa được ghi nhận.
David Ford

4
Chạy Netsh yêu cầu quản trị viên; (
superfly71

27

Tôi có thể làm cho nó chạy mà không có chế độ quản trị viên? nếu có thì thế nào? Nếu không, làm cách nào để ứng dụng thay đổi chế độ quản trị sau khi bắt đầu chạy?

Bạn không thể, nó phải bắt đầu với các đặc quyền nâng cao. Bạn có thể khởi động lại nó bằng runasđộng từ, điều này sẽ nhắc người dùng chuyển sang chế độ quản trị viên

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

EDIT: thực sự, điều đó không đúng; HttpListener có thể chạy mà không có đặc quyền nâng cao, nhưng bạn cần cấp quyền cho URL mà bạn muốn nghe. Xem câu trả lời của Darrel Miller để biết chi tiết.


Bạn có thể giải thích lý do tại sao tôi phải bắt đầu với các đặc quyền nâng cao?
Randall Flagg

Bạn cũng cần thêm dòng này 'startInfo.UseShellExecute = false;' trước 'Process.Start (startInfo);'
Randall Flagg

@Randall: bởi vì đó là cách Windows hoạt động ... một quy trình không thể chuyển sang chế độ quản trị viên trong khi nó đang chạy. Về UseShellExecute: nó phụ thuộc vào những gì bạn đang thực thi. Tôi đã kiểm tra mã của mình với "notepad.exe", nó hoạt động tốt mà không cần UseShellExecute = false
Thomas Levesque

Cảm ơn. Giới thiệu về UseShellExecute: Tôi đã cố chạy mã tôi đã đăng. Một vấn đề khác là vì một số lý do, nó đã hỏi tôi một lần nếu tôi muốn chạy với tư cách quản trị viên và bất kỳ lúc nào sau đó nó không hỏi. Tôi khởi động lại, Gỡ lỗi để đảm bảo nó đi đến đó và không có gì. bất kỳ đề xuất?
Randall Flagg

1
@Thomas Không có vấn đề gì khi chạy HttpListener trong chế độ không phải quản trị viên.
Darrel Miller

23

Nếu bạn sử dụng http://localhost:80/làm tiền tố, bạn có thể nghe các yêu cầu http mà không cần đặc quyền Quản trị.


2
Bạn có thể gửi một số mã ví dụ? Tôi vừa thử điều này http://localhost:80/và nhận được "Truy cập bị từ chối".
John Saunders

2
Xin lỗi, điều đó dường như không hoạt động. Vui lòng kiểm tra xem bạn có đang chạy với tư cách quản trị viên không, đây không phải là trường hợp bình thường.
Jonno

Nếu điều này hoạt động, tôi sẽ coi đó là một lỗi của windows. bất kể bạn nghĩ gì về windows, tôi cho rằng đây là một mức độ rất cơ bản mà rất, rất có thể họ đã không bỏ lỡ để xử lý chính xác.
hoijui

26
Vì vậy, hơn 2 năm sau, điều này đối với tôi bây giờ trên Windows Server 2008 R2 với .NET framework 4.5. httpListener.Prefixes.Add("http://*:4444/");thực sự cho thấy một Access Deniedlỗi nhưng httpListener.Prefixes.Add("http://localhost:4444/");làm việc mà không có bất kỳ vấn đề. Có vẻ như Microsoft loại trừ localhostkhỏi những hạn chế này. Thật thú vị, httpListener.Prefixes.Add("http://127.0.0.1:4444/");vẫn hiển thị lỗi Truy cập bị từ chối, vì vậy điều duy nhất hoạt động làlocalhost:{any port}
Tony

1
@Tony cảm ơn bình luận của bạn thực sự có giá trị nhất đối với tôi. Tôi đã có "127.0.0.1" trong một số cấu hình. QA báo cáo rằng nó không hoạt động trên Windows 8.1 nhưng trong Windows 10 vẫn ổn (và tôi đã tự kiểm tra nó trong Win10). Điều kỳ lạ là có vẻ như Microsoft đã cấp Mọi người để sử dụng 127.0.0.1 trong Win10 nhưng không phải là 8.1 (và có lẽ là các phiên bản trước đó), nhưng chắc chắn, localhost vẫn ổn. Cảm ơn
Adam Plocher

20

Cú pháp sai đối với tôi, bạn phải bao gồm các trích dẫn:

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

nếu không tôi đã nhận được "Tham số không chính xác"


4
"Tham số không chính xác" cũng có thể được trả về nếu bạn không chỉ định dấu vết /trên url
LostSalad

13

Trong trường hợp bạn muốn sử dụng cờ "user = Mọi người", bạn cần điều chỉnh nó theo ngôn ngữ hệ thống của mình. Trong tiếng Anh, nó như được đề cập:

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

Trong tiếng Đức nó sẽ là:

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

1
Trong các hệ thống tiếng Tây Ban Nha, do: user = Todos
Rodrigo Coleues

Thêm vào đó, tôi cần đặt URL trong dấu ngoặc kép, như đã đề cập trong câu trả lời khác.
Carsten Führmann

10

Để thay thế không yêu cầu độ cao hoặc Netsh, bạn cũng có thể sử dụng TcpListener chẳng hạn.

Sau đây là đoạn trích được sửa đổi của mẫu này: 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

Các phương thức đọc và viết là:

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

Điều này thực sự hữu ích vì chúng tôi đã gặp phải vấn đề HTTPListener khi triển khai một IBrowser cho thư viện IdentityModel.OidcClient nên trường hợp sử dụng rất giống với trường hợp trên.
mackie

1
Điều đáng chú ý là mã ở trên có lỗi trong đó. Đầu tiên, việc sử dụng StreamWriter với UTF8 gây ra sự cố trong một số trình duyệt, tôi đoán là do BOM là đầu ra hoặc đại loại như thế. Tôi đã đổi nó thành ASCII và tất cả đều tốt. Tôi cũng đã thêm một số mã để chờ dữ liệu có sẵn để đọc trên luồng vì đôi khi nó sẽ không trả lại dữ liệu.
mackie

Cảm ơn @mackie - phần đó được lấy trực tiếp từ mẫu của chính Microsoft. Tôi đoán họ đã không thực sự kiểm tra nó đúng cách. Nếu bạn có thể chỉnh sửa câu trả lời của tôi, hãy tiếp tục và sửa các phương pháp - nếu không có thể gửi cho tôi các thay đổi và tôi có thể cập nhật nó.
Michael Olsen

2
Thay vì sử dụng ASCII, người ta nên sử dụng hàm tạo sau new UTF8Encoding(false), vô hiệu hóa phát ra BOM. Tôi đã thay đổi điều này trong câu trả lời
Sebastian

Tôi thích giải pháp này hơn câu trả lời được chấp nhận. Chạy Netsh có vẻ như một hack. Đây là một giải pháp lập trình vững chắc. Cảm ơn!
Jim Gomes

7

Bạn có thể bắt đầu ứng dụng của mình với tư cách quản trị viên nếu bạn thêm Bản kê khai ứng dụng vào dự án của mình.

Chỉ cần thêm mục mới vào dự án của bạn và chọn "Tệp kê khai ứng dụng". Thay đổi thành <requestedExecutionLevel>phần thành:

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

7

Theo mặc định, Windows xác định tiền tố sau có sẵn cho mọi người: http: // +: 80 / Tạm thời_Listen_Addresses /

Vì vậy, bạn có thể đăng ký HttpListenerthông qua:

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

Điều này đôi khi gây ra sự cố với phần mềm như Skype sẽ cố gắng sử dụng cổng 80 theo mặc định.


1
Skype là thủ phạm. Tôi đã thay đổi cổng để không là 80 và nó làm việc.
GoTo


2

Tôi cũng gặp phải vấn đề tương tự. Nếu bạn đã bảo lưu url thì trước tiên bạn phải xóa url để chạy ở chế độ không phải quản trị viên nếu không nó sẽ bị lỗi với Access is Denied.

netsh http delete urlacl url=http://+:80
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.