SignalR 2.0 CORS Chrome 禁用 Web 安全奇怪行为

Posted

技术标签:

【中文标题】SignalR 2.0 CORS Chrome 禁用 Web 安全奇怪行为【英文标题】:SignalR 2.0 CORS Chrome Disable Web Security Strange Behavior 【发布时间】:2013-12-20 04:42:45 【问题描述】:

我正在努力让 CORS 与 SignalR 2.0.0 一起使用 - 我知道有很多关于它的 SO 帖子,我一直在浏览它们。

我在 Chrome 中使用禁用的网络安全进行测试时遇到了一个问题,希望有人能解释一下。

我有两个独立的 MVC5 站点(独立的解决方案)在 IIS Express(不同的端口)上运行。其中之一是主机服务器,它安装了 SignalR。主机的Startup.cs 如下所示:

public void Configuration(IAppBuilder app)
        
            app.Map("/signalr", map =>
            
                map.UseCors(CorsOptions.AllowAll);
                var hubConfiguration = new HubConfiguration
                
                   // EnableJSONP = true                    
                   // EnableDetailedErrors = true                                     
                ;
                map.RunSignalR(hubConfiguration);
            );
        

我在主机上只有一个集线器,只有一种方法。 另一个 MVC5 站点是客户端,它具有以下 javascript

jquery-1.10.2.min.js jquery.signalR-2.0.0.min.js 对 host MVC 站点上的 ~/signalr/hubs JavaScript 的引用

它调用的JS如下(相当标准):

$.connection.hub.logging = true;
var chat = $.connection.chatHub;

    chat.client.broadcastMessage = function (name, message) 
        var encodedName = $('<div />').text(name).html();
        var encodedMsg = $('<div />').text(message).html();
        $('#discussion').append('<li><strong>' + encodedName
            + '</strong>:&nbsp;&nbsp;' + encodedMsg + '</li>');
    ;
    $('#displayname').val(prompt('Enter your name:', ''));
    $('#message').focus();

    $.connection.hub.url = 'http://localhost:60601/signalr'; // <-- pointing to the host MVC5 site
    $.connection.hub.start().done(function () 
        console.log("Connected, transport = " + $.connection.hub.transport.name);
        $('#sendmessage').click(function () 
            chat.server.send($('#displayname').val(), $('#message').val());
            $('#message').val('').focus();
        );
    ).fail(function (data) 
        console.log('[Hub Start FAILED]' + data);
    );

编辑发布日志

当我使用上面的代码时,我的主机站点上没有错误。在客户端站点上,我在 Chrome DevTools 控制台中收到以下错误:

[18:06:41 GMT-0600 (Central Standard Time)] SignalR: Auto detected cross domain url. 
[18:06:41 GMT-0600 (Central Standard Time)] SignalR: Client subscribed to hub 'chathub'.
[18:06:41 GMT-0600 (Central Standard Time)] SignalR: Negotiating with 'http://localhost:60601/signalr/negotiate?connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&clientProtocol=1.3'.
MLHttpRequest cannot load http://localhost:60601/signalr/negotiate?connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&clientProtocol=1.3&_=1386201997462. The 'Access-Control-Allow-Origin' header contains the invalid value 'http://localhost:50709, *'. Origin 'http://localhost:50709' is therefore not allowed access.
SignalR error: Error: Error during negotiation request.
[18:06:41 GMT-0600 (Central Standard Time)] SignalR: Stopping connection.

如果我通过添加参数来更改客户端 javascript 中集线器的启动,例如 $.connection.hub.start( jsonp: true, transport: 'webSockets' )(如果我没有将 webSockets 指定为传输,则 SignalR 成功使用长轮询传输),然后我得到以下错误日志.

客户端(Chrome DevTools 控制台)

[18:06:41 GMT-0600 (Central Standard Time)] SignalR: Auto detected cross domain url. 
[18:06:41 GMT-0600 (Central Standard Time)] SignalR: Client subscribed to hub 'chathub'.
[18:12:16 GMT-0600 (Central Standard Time)] SignalR: Negotiating with 'http://localhost:60601/signalr/negotiate?connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&clientProtocol=1.3'
[18:12:17 GMT-0600 (Central Standard Time)] SignalR: Connecting to websocket endpoint 'ws://localhost:60601/signalr/connect?transport=webSockets&connectionToken=GzcGbVs35BV0AFtJ2%2BJQqEYlQfW6ejf6CJ8QI73zfG8OsonD0LIzDmtzNBWM6jSHiD0ptuStjmyOKTpszMFJOgVZoBLuYN08TYx1WUbdNqKDLRZ35Vozmk666%2F7x3rjgowmXhTjX0pVlx8nDY%2FWrpA%3D%3D&connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&tid=4'
[18:12:17 GMT-0600 (Central Standard Time)] SignalR: Websocket opened.
[18:12:22 GMT-0600 (Central Standard Time)] SignalR: webSockets timed out when trying to connect.
[18:12:22 GMT-0600 (Central Standard Time)] SignalR: Closing the Websocket.
SignalR error: Error: No transport could be initialized successfully. Try specifying a different transport or none at all for auto initialization.
[18:12:22 GMT-0600 (Central Standard Time)] SignalR: Stopping connection.
[18:12:22 GMT-0600 (Central Standard Time)] SignalR: Fired ajax abort async = true. 

和主机端(在VS2013输出窗口中):

SignalR.Transports.TransportHeartBeat Information: 0 : Connection 3e856309-8396-481e-8d32-b4085a93985f is New.
SignalR.Transports.WebSocketTransport Information: 0 : CloseSocket(3e856309-8396-481e-8d32-b4085a93985f)
SignalR.Transports.TransportHeartBeat Verbose: 0 : 3e856309-8396-481e-8d32-b4085a93985f is dead
SignalR.Transports.WebSocketTransport Information: 0 : Abort(3e856309-8396-481e-8d32-b4085a93985f)
SignalR.Transports.TransportHeartBeat Information: 0 : Removing connection 3e856309-8396-481e-8d32-b4085a93985f
SignalR.Transports.WebSocketTransport Information: 0 : End(3e856309-8396-481e-8d32-b4085a93985f)
SignalR.Transports.WebSocketTransport Verbose: 0 : DrainWrites(3e856309-8396-481e-8d32-b4085a93985f)
SignalR.Transports.WebSocketTransport Information: 0 : CompleteRequest (3e856309-8396-481e-8d32-b4085a93985f)

问题

我在 Chrome 禁用网络安全模式下启动 host 站点,而客户端站点在 Chrome 中正常启动,但我仍然收到 CORS 错误并且连接断开。

我在 Chrome 禁用网络安全模式下启动 客户端 站点,在 Chrome 中正常启动主机站点,我没有遇到 CORS 问题,并且连接正常。

(如果两个站点也都在禁用 Chrome 的网络安全模式下运行,效果会很好,但这并不奇怪)

问题

当主机禁用其安全性而客户端没有禁用安全性时,我希望不会遇到任何 CORS 问题 - 而不是相反。为什么会这样?

【问题讨论】:

当出现 CORS 错误并断开连接时,您能否发布 SignalR 日志?另外,您如何“在 Chrome 中启动主机站点”?主机站点是只运行 SignalR 还是也提供 HTML? @halter73 我发布了日志。我通过在 Visual Studio 中调试 (F5) 我的 MVC5 站点在 Chrome 中启动该站点。主机站点也提供 HTML 服务 - 新的 ASP.Net MVC5 站点附带的任何默认内容。 【参考方案1】:

问题似乎出在您的 Access-Control-Allow-Origin 标头上。

map.UseCors(CorsOptions.AllowAll);

以上行应导致对 SignalR CORS 请求的每个响应都具有以下标头:

Access-Control-Allow-Origin: (request origin)

在您的情况下,(请求来源)应该是http://localhost:50709,但根据以下日志输出判断,它是http://localhost:50709, *

[18:06:41 GMT-0600 (Central Standard Time)] SignalR: Negotiating with 'http://localhost:60601/signalr/negotiate?connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&clientProtocol=1.3'.
HTMLHttpRequest cannot load http://localhost:60601/signalr/negotiate?connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&clientProtocol=1.3&_=1386201997462.
The 'Access-Control-Allow-Origin' header contains the invalid value 'http://localhost:50709, *'. Origin 'http://localhost:50709' is therefore not allowed access.
SignalR error: Error: Error during negotiation request.

这很可能是由您的 web.config 中类似以下内容引起的:

<httpProtocol>
     <customHeaders>
         <add name="Access-Control-Allow-Origin" value="*" />
     </customHeaders>
</httpProtocol>

或者在您的中间件代码中可能类似于以下内容:

Response.AppendHeader("Access-Control-Allow-Origin", "*");

无论哪种方式,既然您已经在使用map.UseCors(CorsOptions.AllowAll);,您不应该在其他地方设置Access-Control-Allow-Origin 标头。

关于 JSONP 的注意事项

最后,我知道您这样做可能只是出于调试目的,但像这样启动连接:$.connection.hub.start( jsonp: true, transport: 'webSockets' ) 可能不是最好的主意。

使用这些设置,协商请求(以及任何长轮询请求)将始终使用 JSONP,而不是 CORS。当您无法让 CORS 工作时,这可能是有道理的,但如果您能让 CORS 工作,您应该使用它而不是启用 JSONP,因为 CORS 允许更细粒度的访问控制配置。

如果您在服务器上将 EnableJSONP 设置为 true,则无论您将 CorsOptions 设置为什么,您都允许在任何网站上运行的 JS 代码访问您的 SignalR 服务。

如果您决定在服务器上启用 JSONP,则没有理由在您传递给 hub.start 的配置对象中指定 jsonp: true,因为如果 CORS 请求失败,SignalR 应该回退并尝试使用 JSONP。

指定transport: 'webSockets' 也是如此。默认情况下,SignalR 将首先尝试使用 WebSocket 传输(如果可用)。

【讨论】:

确实是&lt;add name="Access-Control-Allow-Origin" value="*" /&gt;。我完全忘记了我在以前的项目中的 IIS Express 设置中有这个。感谢您的提示!

以上是关于SignalR 2.0 CORS Chrome 禁用 Web 安全奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

使用针对 signalR 端点的不同策略覆盖全局 CORS 策略

SignalR、ASP.NET 5 和 CORS

在 SignalR 协商阶段发生 CORS 错误

Azure 移动服务 Web Api 上的 SignalR CORS

SignalR 获取的访问已被 CORS 阻止

Angular 和 .NET Core 的 SignalR CORS 问题