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

5
我有一个应用程序,使用c#编写,基于Asp.Net MVC 5框架。
我正在使用SignalR 2.2.2创建WebSocket通信,在服务器和浏览器之间推送消息。
然而,我需要能够访问已登录用户的ClaimsIdentity对象,以便确定要播客的消息。
通常,我会这样访问身份声明。
IPrincipal user = System.Web.HttpContext.Current.User
IIdentity identity = user.Identity;
var claims = (IEnumerable<Claim>)identity.Claims;

然而,这一行 System.Web.HttpContext.Current 返回 null;这阻止了我获取当前已登录用户的操作。
我猜测 SignalR 创建了一个同步连接,这就是为什么 System.Web.HttpContext.Current 为空的原因。
我也尝试使用 HubCallerContext,如 SO Question 所建议的,但 Context 对象也为空。
System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();

如何在我的Hub中正确访问用户声明?
由于我使用的是Framework 4.5.1,因此我将以下密钥添加到Web.config中的appSettings中。
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>

你正在使用SignalR hubs吗?有一个Context.User属性,你有检查过吗? - Zabavsky
@Zabavsky,Context对象本身为空。 - Junior
是的,Hub类的构造函数。 - Junior
1
这方面有文档,可以在这里查看:https://learn.microsoft.com/en-us/aspnet/signalr/overview/security/hub-authorization。你是否正确注释了你的hub方法? - Brendan Green
@BrendanGreen 是的,我已经添加了它,但问题仍然存在。 - Junior
显示剩余5条评论
2个回答

3
如果使用SignalR hubs,并且希望授权同一Hub下的方法,就必须在Hub上使用Authorize属性。例如:
[Authorize]
public class MessagingHub: Hub
{
    public Task Send(string data)
    {
        return Clients.Caller.SendAsync("Send", "Data To Send");
    }
}

为了在上面的Hub方法中访问声明或用户身份,例如上面的Send,则需要以下内容:
[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");
  }
}

如果使用Json Web Tokens(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.
      });

    }
}

注意:我正在使用 .Net Core 2.1,请确保注册 Hub。 同时,假设 SignalR 已经设置好了。
对于 .Net Core,请确保在 StartUp.cs 中有以下内容;
services.AddSignalR(); // under ConfigureServices

并且

app.UseWebSockets();
app.UseAuthentication();
app.UseSignalR(routes => {
    routes.MapHub<LoopyHub>("/messaging");
});

注意:从SignalR的GitHub问题中,我意识到上面的顺序很重要,以防有人开发出任何问题。也就是说,据我理解,上述顺序是正确的。
从技术上讲,我已经回答了上面的问题,但从.NET Core和Angular的角度来看,我想大多数实现都遵循相同的方法。

不回答问题。问题是关于Asp.Net MVC 5而不是ASPNET Core。 - Nathan Smith

0

你们都忽略了一个重要的事情。

与控制器不同,你无法在 Hub 的构造函数中访问声明。请在连接后访问声明,如下所示。

 [Authorize]
    public class YourHub: Microsoft.AspNetCore.SignalR.Hub
    {

          public override async Task OnConnectedAsync()
          {
             ...
             var identity = (ClaimsIdentity)Context.User.Identity;
          }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接