HttpListener访问被拒绝


179

我正在用C#编写HTTP服务器。

当我尝试执行功能时,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编写它。它不需要管理员权限
Vlad17年

我遇到相同的问题,在Visual Studio 2008 + Windows 7中,它会产生“访问被拒绝”错误,以解决此问题,这是在Admin模式下运行Visual Studio 2008
Mark Khor

Answers:


307

是的,您可以在非管理员模式下运行HttpListener。您需要做的就是授予对特定URL的权限。例如

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

文档在这里


48
这很有用,但是为了完整httpListener.Prefixes.Add("http://*:4444/");起见,此代码行中指定的URL:必须与netsh命令中的URL 完全匹配。例如,我拥有httpListener.Prefixes.Add("http://127.0.0.1:80/");netsh您拥有的相同命令,并且HttpListenerException仍将被抛出。我需要更改httpListener.Prefixes.Add("http://+:80/");感谢您对@Darrel Miller的帮助,因为您使我走上了正确的道路!
psyklopz 2012年

10
而且,如果“ MyUri”为空,请不要忘记在斜杠后面加上斜杠,否则会出现The parameter is incorrect错误。范例:url=http://+:80/
Igor Brejc'13年

14
对于非管理用户,有没有办法做到这一点http://localhost:80/?我有一个桌面应用程序,需要在这样的URL上接收一个请求,仅出于此目的,要求管理员将其安装在50个桌面上似乎很可耻。
约翰·桑德斯

5
显然不是。在文档页面中也没有提到需要提升或管理特权。因此,很容易假设这将充当“更智能”的TCPListener,而实际上有主要理由不使用它-但这没有记录。
戴维·福特

4
运行netsh需要管理员;(
superfly71 '18

27

我可以在没有管理员模式的情况下运行它吗?如果是,怎么办?如果没有,在开始运行后如何将应用程序更改为管理模式?

您不能,它必须以提升的特权开始。您可以使用runas动词重新启动它,该动词将提示用户切换到管理模式

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

编辑:实际上,那不是真的;HttpListener可以在没有提升特权的情况下运行,但是您需要为要侦听的URL授予权限。有关详细信息,请参见Darrel Miller的答案


您能解释一下为什么我必须从特权提升开始吗?
兰德尔·弗拉格

您还需要添加此行'startInfo.UseShellExecute = false;'。在“ Process.Start(startInfo);”之前
兰德尔·弗拉格

@Randall:因为这就是Windows的工作方式...进程运行时无法切换到管理模式。关于UseShellExecute:取决于您执行的内容。我使用“ notepad.exe”测试了我的代码,没有UseShellExecute = false可以正常工作
Thomas Levesque 2010年

谢谢。关于UseShellExecute:我试图运行我发布的代码。另一个问题是,由于某种原因,它询问了我是否要以管理员身份运行,之后又再询问一次。我重新启动,调试,以确保它在那里,什么也没有。有什么建议?
兰德尔·弗拉格

1
@Thomas在非管理员模式下运行HttpListener没问题。
Darrel Miller

23

如果http://localhost:80/用作前缀,则无需管理权限就可以侦听http请求。


2
您可以发布一些示例代码吗?我只是尝试使用此方法,http://localhost:80/并获得了“访问被拒绝”。
约翰·桑德斯

2
抱歉,这似乎不起作用。请检查您是否以管理员身份运行,这不是正常情况。
2014年

如果这将工作,我会认为这是一个Windows错误。无论您如何看待Windows,我都认为这是一个非常基本的水平,很有可能他们不会错过正确处理的东西。
hoijui 2015年

26
因此,超过2年后,这对我现在在带有.NET Framework 4.5的Windows Server 2008 R2上适用。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中进行了测试)。奇怪的是,似乎微软已经授予所有人在Win10中使用127.0.0.1的权限,但不能在8.1(可能是以前的版本)中使用,但可以肯定的是,localhost很好。谢谢
Adam Plocher '16

20

语法对我来说是错误的,您必须添加引号:

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

否则,我会收到“参数不正确”


4
如果您未/在网址上指定结尾,也可能会返回“参数不正确”
LostSalad

13

如果要使用标志“ user = Everyone”,则需要将其调整为系统语言。用英语是这样提到的:

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

用德语是:

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

1
在西班牙语系统中,请执行以下操作:user = Todos
Rodrigo Rodrigues

另外,如另一个答案中所述,我需要将URL放在引号中。
卡斯滕·弗曼(CarstenFü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);
        }
    });
}

这非常有用,因为在为IdentityModel.OidcClient库实现IBrowser时遇到HttpListener问题,因此与上述情况非常相似。
mackie

1
值得注意的是,上面的代码虽然有错误。首先,将StreamWriter与UTF8一起使用会导致某些浏览器出现问题,我猜测是因为正在输出BOM或类似的东西。我将其更改为ASCII,一切都很好。我还添加了一些代码来等待数据变为可在流中读取,因为有时它不返回任何数据。
mackie's

感谢@mackie-这一部分实际上直接取自Microsoft自己的示例。我猜他们并没有真正对其进行正确的测试。如果您可以编辑我的答案,请继续并解决问题的方法-否则,请将更改发送给我,然后我可以对其进行更新。
迈克尔·奥尔森

2
而不是使用ASCII,应该使用以下构造函数new UTF8Encoding(false),该构造函数禁止发出BOM。我已经在答案中更改了此设置
塞巴斯蒂安

与可接受的答案相比,我更喜欢这种解决方案。运行netsh似乎很容易。这是一个可靠的程序化解决方案。谢谢!
吉姆·戈麦斯

7

如果将Application Manifest添加到项目中,则可以以管理员身份启动应用程序。

只需将New Item添加到您的项目中,然后选择“ 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,并且可以正常工作。
转到


2

我也遇到过类似的问题。如果您已经保留了url,则必须先删除该url才能在非管理员模式下运行,否则它将因Access is Denied错误而失败。

netsh http delete urlacl url=http://+:80
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.