7

ASP.NET MVC 5 と SignalR を使用しています。特定のユーザーにメッセージを送信したい。このチュートリアルで説明されている方法に従いました(この回答でも提案されています)。

connectionIdとしてIUserIdProvider使用するためにオーバーライドしました。UserId

public class SignalRUserIdProvider : IUserIdProvider
{
    public string GetUserId(IRequest request)
    {
        // use: UserId as connectionId
        return Convert.ToString(request.User.Identity.GetUserId<int>());
    }
}

Startupそして、上記のカスタム プロバイダーを使用するようにアプリを変更しました。

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var idProvider = new SignalRUserIdProvider();
        GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider);
        ConfigureAuth(app);
        app.MapSignalR();
    }
}

これで、クライアントは送信先のClientIDをハブに渡すことができ、ハブは要求されたクライアントにのみメッセージを転送します。これが私のハブです。

[Authorize] 
public class ChatHub : Hub
{
    public void Send(string message, string destClientId)
    {
        Clients.User(destClientId).messageReceived(Context.User.Identity.Name + " says: " + message);
    }
}

これは完全に正常に機能しています。私の質問はセキュリティに関するもので、これが安全な Web サイトにとって正しいアプローチであるかどうかを教えてください。

Introduction to SignalR Securityによると、ランダムに生成された接続 ID は SignalR セキュリティの一部です。

サーバーは、ユーザー名と一致しない接続 ID からの要求を処理しません。悪意のあるユーザーは、ユーザー名と現在のランダムに生成された接続 ID を知っている必要があるため、有効な要求を推測できる可能性はほとんどありません。

上記のアプローチは、ランダムに選択されたconnectionIdを固定のものに置き換えていUserIdます...上記のコードにセキュリティ上の問題はありますか?


注:私は、ユーザーがオフラインであってもメッセージを受信できる必要がある電子商取引Webサイトに取り組んでいます(メッセージはDBに保存され、オンラインになると読み取ることができます)。

4

3 に答える 3

1

更新日

注: 私は、ユーザーがオフラインであってもメッセージを受信できる必要がある e コマース Web サイトで作業しています (メッセージは DB に保存され、オンラインになると読み取ることができます)。

安全な方法でランダムに生成された connectionId を使用してメッセージを送信するには、次の手順を実行します。

最初に次のクラスを追加して、ユーザーの connectionIds を追跡し、ユーザーがオンラインかオフラインかを把握します。

public static class ChatHubUserHandler
{
    public static ConcurrentDictionary<string, ChatHubConnectionViewModel> ConnectedIds =
        new ConcurrentDictionary<string, ChatHubConnectionViewModel>(StringComparer.InvariantCultureIgnoreCase);
}

public class ChatHubConnectionViewModel
{
    public string UserName { get; set; }
    public HashSet<string> UserConnectionIds { get; set; }
}

次のように設定ChatHubします

クラスにChatHubセキュアな追加[Authorize]属性を作成するには。ChatHub

[Authorize]
public class ChatHub : Hub
{
    private string UserName => Context.User.Identity.Name;
    private string ConnectionId => Context.ConnectionId;

    // Whenever a user will be online randomly generated connectionId for
    // him be stored here.Here I am storing this in Memory, if you want you
    // can store it on database too.
    public override Task OnConnected()
    {

        var user = ChatHubUserHandler.ConnectedIds.GetOrAdd(UserName, _ => new ChatHubConnectionViewModel
        {
            UserName = UserName,
            UserConnectionIds = new HashSet<string>()
        });

        lock (user.UserConnectionIds)
        {
            user.UserConnectionIds.Add(ConnectionId);
        }

        return base.OnConnected();
    }


    // Whenever a user will be offline his connectionId id will be
    // removed from the collection of loggedIn users.

    public override Task OnDisconnected(bool stopCalled)
    {
        ChatHubConnectionViewModel user;
        ChatHubUserHandler.ConnectedIds.TryGetValue(UserName, out user);

        if (user != null)
        {
            lock (user.UserConnectionIds)
            {
                user.UserConnectionIds.RemoveWhere(cid => cid.Equals(ConnectionId));
                if (!user.UserConnectionIds.Any())
                {
                    ChatHubUserHandler.ConnectedIds.TryRemove(UserName, out user);
                }
            }
        }

        return base.OnDisconnected(stopCalled);
    }
}

次のモデル クラスを使用して、メッセージをデータベースに格納します。必要に応じてメッセージ クラスをカスタマイズすることもできます。

public class Message
{

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long MessageId { get; set; }

    [ForeignKey("Sender")]
    public string SenderId { get; set; }

    [ForeignKey("Receiver")]
    public string ReceiverId { get; set; }

    [Required]
    [DataType(DataType.MultilineText)]
    public string MessageBody { get; set; }
    public DateTime MessageSentAt { get; set; }
    public bool IsRead { get; set; }


    public User Sender { get; set; }
    public User Receiver { get; set; }
}

次に、メッセージ コントローラーで次のようにします。

これは単なるコード例です。必要に応じてコードをカスタマイズできます。

[HttpPost]
public async Task<ActionResult> SendMessage(string messageBody, string receiverAspNetUserId)
{
      string loggedInUserId = User.Identity.GetUserId();
      Message message = new Message()
      {
            SenderId = loggedInUserId,
            ReceiverId = receiverAspNetUserId,
            MessageBody = messageBody,
            MessageSentAt = DateTime.UtcNow
      };

      _dbContext.Messages.Add(message);
      _dbContext.SaveChangesAsync();


      // Check here if the receiver is currently logged in. If logged in,
      // send push notification. Send your desired content as parameter
      // to sendPushNotification method.

      if(ChatHubUserHandler.ConnectedUsers.TryGetValue(receiverAspNetUserId, out ChatHubConnectionViewModel connectedUser))
      {
            List<string> userConnectionIds = connectedUser.UserConnectionIds.ToList();
            if (userConnectionIds.Count > 0)
            {
                var chatHubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
                chatHubContext.Clients.Clients(userConnectionIds).sendPushNotification();
            }
      }

      return Json(true);
}

ここで問題は、受信者がオフラインのときにメッセージが送信された場合はどうなるかということです。

わかった!この場合、プッシュ通知を 2 つの方法で処理できます。受信者がオンラインになるとすぐに ajax メソッドまたは SignalR Hub メソッドを呼び出して、通知をバインドします。別の方法は、レイアウト ページの通知領域の部分ビューを使用することです。個人的には、通知領域に部分ビューを使用することを好みます。

これがあなたを助けることを願っています!

于 2018-09-06T02:54:55.667 に答える