简体   繁体   中英

403 (Forbidden) response from SignalR Hub using ASP.NET hosting on IIS server

I'm hosting a SignalR Hub on Windows Server 2012 with IIS as an ASP.NET Web application that I've tested successfully on my local machine. But when I publish and try to connect from a Angular application the server responds with 403 Forbidden on the /negotiate request. The Angular application is located on a different domain then the Hub server.

I've read that this is caused by a CORS issue, but I've tried every solution I can find without any change. Can it be a IIS server issue or have I missed something in my code?

The route being called is https://example.com/signalr/negotiate

SignalR Server:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Map("/signalr", map =>
        {
            map.UseCors(CorsOptions.AllowAll);

            var hubConfiguration = new HubConfiguration
            {
                EnableJSONP = true,
                EnableDetailedErrors = true
            };

            map.RunSignalR(hubConfiguration);
        });
    }
}

// Hub that handles Online user list
public class OnlineHub : Hub
{
    private static List<AppUserDto> _usersOnline = new List<AppUserDto>();

    public OnlineHub()
    {
        // Automapper Setup
        MappingConfig.Init();
    }

    public override Task OnConnected()
    {
        var user = GetUser();
        _usersOnline.Add(user);
        Clients.All.listUpdated(_usersOnline);

        return base.OnConnected();
    }

    public override Task OnReconnected()
    {
        var user = GetUser();

        // Add user to list of online users if it doesn't exist
        if (!_usersOnline.Any(u => u.Email == user.Email))
        {
            _usersOnline.Add(user);
            Clients.All.listUpdated(_usersOnline);
        }

        return base.OnReconnected();
    }

    public override Task OnDisconnected(bool stopCalled)
    {
        var user = GetUser();
        if (!_usersOnline.Any(u => u.Email == user.Email))
        {
            // Remove user from list of online users
            _usersOnline.Remove(user);

            Clients.All.listUpdated(_usersOnline);
        }

        return base.OnDisconnected(stopCalled);
    }

    private AppUserDto GetUser()
    {
        using (var db = new EntityDbContext())
        {
            // Get connected AppUserDto
            var user = db.AppUsers.FirstOrDefault(u => u.UserName == Context.User.Identity.Name);
            // Add user to list of online users
            if (user != null)
            {
                return Mapper.Map<AppUserDto>(user);
            }

            return null;
        }
    }
}

Angular Application SignalR Service

import { AppSettings } from './../app.settings';
import { EventEmitter, Injectable } from '@angular/core';

declare const $: any;

@Injectable()
export class SignalRService {
  // Declare the variables
  private proxy: any;
  private connection: any;
  private authData: any;
  // create the Event Emitter
  public messageReceived: EventEmitter<any>;
  public connectionEstablished: EventEmitter<Boolean>;
  public connectionExists: Boolean;

  constructor(private appSettings: AppSettings) {
    // Setup
    this.connectionEstablished = new EventEmitter<Boolean>();
    this.messageReceived = new EventEmitter<any>();
    this.connectionExists = false;
  }

  public initialize(proxyName: string): void {
    this.connection = $.hubConnection(this.appSettings.SIGNALR_BASE_URL);
    this.proxy = this.connection.createHubProxy(proxyName);
    this.registerOnServerEvents();
    this.startConnection();
  }

  private startConnection(): void {
    this.connection.start({withCredentials: false})
      .done((data: any) => {
        console.log('SignalR Connected with: ' + data.transport.name);
        this.connectionEstablished.emit(true);
        this.connectionExists = true;
      })
      .fail((error: any) => {
        console.log('SignalR could not connect: ' + error);
        this.connectionEstablished.emit(false);
      });
  }

  private registerOnServerEvents() {
    this.proxy.on('listUpdated', (list: any) => {
      console.log(list);
      this.messageReceived.emit(list);
    });
  }
}

initialize(proxyName) gets called from a controller to start a connection to the Hub.

UPDATE

I've tried to rebuild the server and Hub using .NET Core 2.0, but when I test that on the IIS server I get:

"Failed to load https://signalr.example.com/online/negotiate : Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin ' https://example.com ' is therefore not allowed access."

So it's still a CORS issue even though I've setup everything just as multiple guides have done.

I've had issues in the past where the api path you are trying to hit is actually a virtual directory, and then IIS returns you a 403 because it thinks you are trying to view / access that directory instead of the webAPI route.

GET api/negotiate will 403 if you have the directory api/negotiate on your server.

This will be the case if you WebApiController is located in your project in a directory like:

/api/negotiate/NegotiateApiController.cs

You can resolve this very easily if that's the case by either changing the route or the directory name.

Note: This will come back as a 405 on some browsers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM