【问题标题】:Inject IPrincipal with SignalR使用 SignalR 注入 IPrincipal
【发布时间】:2015-02-13 13:46:30
【问题描述】:

我正在尝试将 IPrincipal 注入到我的 SignalR 集线器构造函数中。 我已经看到并尝试了来自“Selective IPrincipal Injection via StructureMap with SignalR”的解决方案,但不幸的是,这似乎不再适用于 SignalR 2.x。

在我的调试中,我发现有时,我的集线器构造函数是在堆栈中使用我的 OWIN 中间件调用的。在这种情况下,Thread.CurrentPrincipal 是正确的值。此外(令人惊讶的是)HttpContext.Current 也不是空的。我的印象是这在 SignalR 中总是空的,我没有尝试使用它,但我只是在观察。这些有效的调用似乎来自调用堆栈中的管道。

其他时候,调用似乎来自线程池。在这些情况下,Thread.CurrentPrincipalGenericPrincipalHttpContext.Current 为空(再次只是观察),我似乎无法静态地了解主体。但是,在集线器内部,this.Context.User 属性确实具有正确的主体。

我怎样才能静态地获取主体,以便我可以将其注入到集线器构造函数中?

【问题讨论】:

  • 为了解决这个问题,我制作了一个 WebAPI:用户可以使用我的 API 加入群组或发送消息。在这个 API 中,我可以检查身份/角色。
  • @Guillaume 这如何解决 SignalR?
  • 这并不能真正解决您的问题,这就是我没有发布答案的原因。这是在用户的所有传入操作上获取 IPrincipal 的一种变通方法。控制器充当客户端和集线器之间的代理。可能不适合实时(游戏,...)。
  • 我认为您不想在线程池线程上设置 IPrincipal。在其他情况下这是错误的。

标签: c# dependency-injection signalr owin iprincipal


【解决方案1】:

预计 HttpContext.CurrentThread.CurrentPrincipal 有时会在 SignalR 集线器激活时设置,但并非总是如此。这是因为激活线程经常使用 ASP.NET 的SynchronizationContext 运行。在某些情况下,情况并非如此,例如当激活 Hub 以处理 WebSocket 消息或不干净的 OnDisconnected 事件时。长话短说,有时这些静态因素恰好存在,但您不能依赖它。

我不知道有什么方法可以可靠地静态获取IPrincipal。在 Hub 中使用 Context.User 有什么问题?

【讨论】:

  • Context.User 依赖于与 SignalR 集线器框架的更紧密耦合,这降低了我(例如)在没有包装函数的情况下在 SignalR 和 Web API 之间共享代码的能力。
【解决方案2】:

如果我正确理解了您要执行的操作...您应该构建自己的 Authorize 属性,这会将自定义主体放入特殊的 Owin var,然后可以在集线器内的 Context.User 中访问它。

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
    {
        //put our custom user-principal into a magic "server.User" Owin variable
        request.Environment["server.User"] = new MyCustomPrincipal(); //<!-THIS!

        return base.AuthorizeHubConnection(hubDescriptor, request);
    }
}

然后将此属性应用到您的 Hub。

如果您想了解更多信息,我在博客上写了 here 并附有更多代码示例

【讨论】:

    【解决方案3】:

    我试图解决同样的问题,但我找到了设置用户身份的解决方案。

    我的应用程序受 saml 保护,客户端应用程序发送“SAML”令牌作为标头的一部分。我们编写了 Asp.net 模块来验证令牌并准备用户身份并为响应标头添加值。

    我创建了 OwinStartup 类,并使用以下代码添加了自己的请求处理器。 我已经为 Longpolling 测试了这段代码并且工作正常。我不确定它在“WebScoket”中是如何工作的。

     public void Configuration(IAppBuilder app)
        {
            // Any connection or hub wire up and configuration should go here           
            try
            {
                app.Use(SetMyPrincipalObject);
            }
        }
    
      private Task SetMyPrincipalObject(IOwinContext arg1, Func<Task> arg2)
        {
            //var p = "Process response";//Process Response Header here and //create identity
            //arg1.Request.User = p;
            //return arg2.Invoke();
        }
    

    【讨论】:

    • 这根本不能回答问题。