在 ASP.NET MVC 5 项目中使用 SignalR Hub 时,如何访问 Identity 2 中用户的声明?

Jun*_*ior 5 c# asp.net asp.net-mvc signalr asp.net-mvc-5

我有一个在 Asp.Net MVC 5 框架之上使用 C# 编写的应用程序。

我正在使用SignalR 2.2.2在浏览器和服务器之间创建 WebSocket 通信,以将消息从服​​务器推送到浏览器。

但是,我需要能够访问ClaimsIdentity登录用户的对象,以便我可以确定要播客的消息。

通常,我会像这样访问身份声明

IPrincipal user = System.Web.HttpContext.Current.User
IIdentity identity = user.Identity;
var claims = (IEnumerable<Claim>)identity.Claims;
Run Code Online (Sandbox Code Playgroud)

然而,这一行System.Web.HttpContext.Current返回 null;阻止我获取当前登录的用户。

我猜测 SignalR 创建了一个同步连接,这就是为什么它System.Web.HttpContext.Current是空的。

我也尝试使用这个SO QuestionHubCallerContex所建议的,但该对象也是空的。Context

System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();
Run Code Online (Sandbox Code Playgroud)

如何在我的 Hub 中正确访问用户声明?

appSettings当我使用 Framework 4.5.1 时,我在 Web.config 中添加了以下键

<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
Run Code Online (Sandbox Code Playgroud)

rey*_*der 3

如果使用 SignalR 集线器并想要授权同一集线器下的方法,则必须使用集线器上的授权属性。IE

[Authorize]
public class MessagingHub: Hub
{
    public Task Send(string data)
    {
        return Clients.Caller.SendAsync("Send", "Data To Send");
    }
}
Run Code Online (Sandbox Code Playgroud)

为了在集线器方法中访问声明或用户身份,例如上面的发送,则需要执行以下操作:

[Authorize]
public class MessagingHub: Hub
{
   public Task Send(string data)
  {
     var identity = (ClaimsIdentity)Context.User.Identity;
     //one can access all member functions or properties of identity e.g, Claims, Name, IsAuthenticated... 
      return Clients.Caller.SendAsync("Send", "Data To Send");
  }
}
Run Code Online (Sandbox Code Playgroud)

如果使用 Json Web 令牌 (JWT) 或仅使用令牌身份验证(就像我当时的情况一样),那么从客户端来看,这可以用来调用 Hub 的 Send 方法。

注意:对于我的情况,客户端是 Angular 6 应用程序。

import { HubConnection } from "@aspnet/signalr";
import * as signalR from '@aspnet/signalr';
...
private _messagingHubConnection: HubConnection | undefined;
public async: any;
...
constructor(){}
...
 SendMessg(): void {
    if (this._messagingHubConnection) {
      this._messagingHubConnection.invoke('Send');
    }
  }
...

ngOnInit(){
    this._messagingHubConnection= new signalR.HubConnectionBuilder()
      .withUrl("messaging", { accessTokenFactory: () => "jwt_token" }) //have a logic that gets the current user's authentication token from the browser 
      .build();

    this._messagingHubConnection.start().then(() => {
        this.SendMessg();
      }).catch(err => console.error(err.toString()));

    if (this._messagingHubConnection) {
      this._messagingHubConnection.on('Send', (data: any) => {
        //data represents response/message received from Hub method'd return value.
      });

    }
}
Run Code Online (Sandbox Code Playgroud)

注意:我使用的是 .Net Core 2.1,因此请务必注册 Hub。这也是假设 signalR 已经设置

对于 .Net Core,请确保您的 StartUp.cs 中有;

services.AddSignalR(); // under ConfigureServices
Run Code Online (Sandbox Code Playgroud)

app.UseWebSockets();
app.UseAuthentication();
app.UseSignalR(routes => {
    routes.MapHub<LoopyHub>("/messaging");
});
Run Code Online (Sandbox Code Playgroud)

注意:从SignalR 的 GitHub 上的问题中,我意识到上述顺序很重要,以防万一有人遇到任何问题。即根据我的理解,上述顺序是正确的。

从技术上讲,我已经回答了上面的问题,但来自 .NET Core 和 Angular 上下文;我想大多数(如果不是全部)实现都遵循相同的方法。