Передать IPrincipal из MVC в SignalR

У меня есть приложение MVC с аутентификацией на основе форм с пользовательским принципалом. Перед использованием приложения пользователь должен войти в систему. После этого я хочу использовать SignalR, проблема в том, что Context.User.Identity.Name всегда пустая строка.

CustomPrincipal.cs

public class CustomPrincipal : IPrincipal
{
    public CustomPrincipal(IIdentity identity)
    {
        Identity = identity;
    }

    public IIdentity Identity { get; }

    public bool IsInRole(string role)
    {
        return true;
    }
}

CustomIdentity.cs

public class CustomIdentity : IIdentity
{
    public CustomIdentity(EmployeeModel user)
    {
        Name = user.Username;
        Id = user.Id;
    }

    public string AuthenticationType => "Custom";

    public bool IsAuthenticated => !string.IsNullOrEmpty(Name);

    public int Id { get; set; }

    public string Name { get; }
}

BaseController.cs (из которого я извлекаю все свои контроллеры MVC)

protected override void OnAuthorization(AuthorizationContext context)
{
    if (SessionPersister.User != null && !string.IsNullOrEmpty(SessionPersister.User.Username))
    {
        context.HttpContext.User = new CustomPrincipal(new CustomIdentity(SessionPersister.User));
    }

    base.OnAuthorization(context);
}

SessionPersister здесь просто статический класс для хранения вошедших в систему пользователей

Итак, все в моем приложении MVC работает отлично. Проблема в том, что когда пользователь вошел в систему, и я хочу отправить сообщение другому пользователю, который вошел в систему через SignalR, Identity.User.Name - пустая строка в моем классе Hub:

public override Task OnConnected()
{
    string name = Context.User.Identity.Name; // it's empty

    return base.OnConnected();
}

Есть ли способ передать мой MVC IPrincipal в SignalR или настроить его для использования моей пользовательской аутентификации, которую я использую в MVC?

Спасибо заранее

1 ответ

Решение

Итак, небольшая логическая ошибка:

BaseController.OnAuthorization срабатывает только когда выполняется контроллер. Когда поступает запрос SignalR, этот метод никогда не будет вызываться для этого запроса.

Таким образом, способ обойти это - переместить код из контроллера в более глобальную область видимости. Например, вы можете использовать Global.asax.cs и добавь это, Likeo:

    protected void Application_PostAuthenticateRequest( object sender, EventArgs e )
    {
        //do your custom principal setting here.
        this.Context.User = new CustomPrincipal( new CustomIdentity( 10, "test" ) );
    }

Тогда в вашем хабе вы сможете увидеть идентичность так:

    public String Hello(String hello)
    {
        //no need to actually cast if you don't need the non-iidentity properties
        //var identity = (CustomIdentity) this.Context.User.Identity;
        //identity.Id == 10
        //identity.Name == "test"

        return hello;
    }

В качестве альтернативы, вместо Global.asax, я уверен, что вы можете добавить его в конвейер OWIN после аутентификации пользователя. Тем не менее, кто-то еще должен будет предоставить точный пример.

РЕДАКТИРОВАТЬ: просто чтобы уточнить, я изменил конструктор для вашего CustomIdentity, так как у меня не было всех ваших классов. Приведенные выше примеры являются просто доказательством понятий.

Другие вопросы по тегам