diff --git a/Realtime/Conversation/LCIMConversation.cs b/Realtime/Conversation/LCIMConversation.cs index 33d4e68..e10c7cf 100644 --- a/Realtime/Conversation/LCIMConversation.cs +++ b/Realtime/Conversation/LCIMConversation.cs @@ -115,8 +115,16 @@ namespace LeanCloud.Realtime { Cid = Id, }; conv.M.AddRange(clientIds); - // TODO 签名参数 - + // 签名参数 + if (client.SignatureFactory != null) { + LCIMSignature signature = client.SignatureFactory.CreateConversationSignature(Id, + client.ClientId, + clientIds, + LCIMSignatureAction.Invite); + conv.S = signature.Signature; + conv.T = signature.Timestamp; + conv.N = signature.Nonce; + } GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Add); request.ConvMessage = conv; GenericCommand response = await client.connection.SendRequest(request); @@ -138,8 +146,16 @@ namespace LeanCloud.Realtime { Cid = Id, }; conv.M.AddRange(removeIds); - // TODO 签名参数 - + // 签名参数 + if (client.SignatureFactory != null) { + LCIMSignature signature = client.SignatureFactory.CreateConversationSignature(Id, + client.ClientId, + removeIds, + LCIMSignatureAction.Kick); + conv.S = signature.Signature; + conv.T = signature.Timestamp; + conv.N = signature.Nonce; + } GenericCommand request = client.NewCommand(CommandType.Conv, OpType.Remove); request.ConvMessage = conv; GenericCommand response = await client.connection.SendRequest(request); @@ -282,6 +298,15 @@ namespace LeanCloud.Realtime { SrcCid = Id, }; blacklist.ToPids.AddRange(clientIds); + if (client.SignatureFactory != null) { + LCIMSignature signature = client.SignatureFactory.CreateBlacklistSignature(Id, + client.ClientId, + clientIds, + LCIMSignatureAction.ConversationBlockClients); + blacklist.S = signature.Signature; + blacklist.T = signature.Timestamp; + blacklist.N = signature.Nonce; + } GenericCommand request = client.NewCommand(CommandType.Blacklist, OpType.Block); request.BlacklistMessage = blacklist; GenericCommand response = await client.connection.SendRequest(request); @@ -296,6 +321,15 @@ namespace LeanCloud.Realtime { SrcCid = Id, }; blacklist.ToPids.AddRange(clientIds); + if (client.SignatureFactory != null) { + LCIMSignature signature = client.SignatureFactory.CreateBlacklistSignature(Id, + client.ClientId, + clientIds, + LCIMSignatureAction.ConversationUnblockClients); + blacklist.S = signature.Signature; + blacklist.T = signature.Timestamp; + blacklist.N = signature.Nonce; + } GenericCommand request = client.NewCommand(CommandType.Blacklist, OpType.Unblock); request.BlacklistMessage = blacklist; GenericCommand response = await client.connection.SendRequest(request); @@ -467,5 +501,16 @@ namespace LeanCloud.Realtime { MemberIdList = conv.M.ToList(); } } + + internal void MergeFrom(Dictionary conv) { + if (conv.TryGetValue("objectId", out object idObj)) { + Id = idObj as string; + } + + if (conv.TryGetValue("unique", out object uniqueObj)) { + + } + + } } } diff --git a/Realtime/LCIMClient.cs b/Realtime/LCIMClient.cs index 7906c6e..196ba35 100644 --- a/Realtime/LCIMClient.cs +++ b/Realtime/LCIMClient.cs @@ -79,8 +79,13 @@ namespace LeanCloud.Realtime { get; set; } - public LCIMClient(string clientId) { + internal ILCIMSignatureFactory SignatureFactory { + get; private set; + } + + public LCIMClient(string clientId, ILCIMSignatureFactory signatureFactory = null) { ClientId = clientId; + SignatureFactory = signatureFactory; conversationDict = new Dictionary(); } @@ -95,7 +100,14 @@ namespace LeanCloud.Realtime { await connection.Connect(); // Open Session GenericCommand request = NewCommand(CommandType.Session, OpType.Open); - request.SessionMessage = new SessionCommand(); + SessionCommand session = new SessionCommand(); + if (SignatureFactory != null) { + LCIMSignature signature = SignatureFactory.CreateConnectSignature(ClientId); + session.S = signature.Signature; + session.T = signature.Timestamp; + session.N = signature.Nonce; + } + request.SessionMessage = session; GenericCommand response = await connection.SendRequest(request); SessionToken = response.SessionMessage.St; } @@ -139,7 +151,7 @@ namespace LeanCloud.Realtime { bool temporary = false, int temporaryTtl = 86400, Dictionary properties = null) { - GenericCommand command = NewCommand(CommandType.Conv, OpType.Start); + GenericCommand request = NewCommand(CommandType.Conv, OpType.Start); ConvCommand conv = new ConvCommand { Transient = transient, Unique = unique, @@ -159,8 +171,14 @@ namespace LeanCloud.Realtime { Data = JsonConvert.SerializeObject(LCEncoder.Encode(properties)) }; } - command.ConvMessage = conv; - GenericCommand response = await connection.SendRequest(command); + if (SignatureFactory != null) { + LCIMSignature signature = SignatureFactory.CreateStartConversationSignature(ClientId, members); + conv.S = signature.Signature; + conv.T = signature.Timestamp; + conv.N = signature.Nonce; + } + request.ConvMessage = conv; + GenericCommand response = await connection.SendRequest(request); string convId = response.ConvMessage.Cid; if (!conversationDict.TryGetValue(convId, out LCIMConversation conversation)) { if (transient) { @@ -334,7 +352,7 @@ namespace LeanCloud.Realtime { OnMessageReceived?.Invoke(null, message); } - private LCIMConversation GetOrCreateConversation(string convId) { + internal LCIMConversation GetOrCreateConversation(string convId) { if (!conversationDict.TryGetValue(convId, out LCIMConversation conversation)) { conversation = new LCIMConversation(this); conversationDict.Add(convId, conversation); diff --git a/Realtime/Realtime.csproj b/Realtime/Realtime.csproj index d90d89d..2944dcf 100644 --- a/Realtime/Realtime.csproj +++ b/Realtime/Realtime.csproj @@ -19,5 +19,6 @@ + diff --git a/Realtime/Signature/ILCIMSignatureFactory.cs b/Realtime/Signature/ILCIMSignatureFactory.cs new file mode 100644 index 0000000..3f4d845 --- /dev/null +++ b/Realtime/Signature/ILCIMSignatureFactory.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; + +namespace LeanCloud.Realtime { + public interface ILCIMSignatureFactory { + /// + /// 登录签名 + /// + /// + /// + LCIMSignature CreateConnectSignature(string clientId); + + /// + /// 创建开启对话签名 + /// + /// + LCIMSignature CreateStartConversationSignature(string clientId, IEnumerable memberIds); + + /// + /// 创建会话相关签名 + /// + /// + /// + /// + /// + /// + LCIMSignature CreateConversationSignature(string conversationId, string clientId, IEnumerable memberIds, string action); + + /// + /// 创建黑名单相关签名 + /// + /// + /// + /// + /// + /// + LCIMSignature CreateBlacklistSignature(string conversationId, string clientId, IEnumerable memberIds, string action); + } +} diff --git a/Realtime/Signature/LCIMSignature.cs b/Realtime/Signature/LCIMSignature.cs new file mode 100644 index 0000000..957a303 --- /dev/null +++ b/Realtime/Signature/LCIMSignature.cs @@ -0,0 +1,19 @@ +namespace LeanCloud.Realtime { + public class LCIMSignature { + public string Signature { + get; set; + } + + public long Timestamp { + get; set; + } + + public string Nonce { + get; set; + } + + public LCIMSignature() { + + } + } +} diff --git a/Realtime/Signature/LCIMSignatureAction.cs b/Realtime/Signature/LCIMSignatureAction.cs new file mode 100644 index 0000000..f59a6eb --- /dev/null +++ b/Realtime/Signature/LCIMSignatureAction.cs @@ -0,0 +1,17 @@ +namespace LeanCloud.Realtime { + public static class LCIMSignatureAction { + // 邀请 + public const string Invite = "invite"; + // 踢出 + public const string Kick = "kick"; + + // + public const string ClientBlockConversations = "client-block-conversations"; + + public const string ClientUnblockConversations = "client-unblock-conversations"; + + public const string ConversationBlockClients = "conversation-block-clients"; + + public const string ConversationUnblockClients = "conversation-unblock-clients"; + } +} diff --git a/Test/RealtimeConsole/LocalSignatureFactory.cs b/Test/RealtimeConsole/LocalSignatureFactory.cs new file mode 100644 index 0000000..e68f38a --- /dev/null +++ b/Test/RealtimeConsole/LocalSignatureFactory.cs @@ -0,0 +1,96 @@ +using System; +using System.Text; +using System.Linq; +using System.Collections.Generic; +using System.Security.Cryptography; +using LeanCloud.Realtime; +using LeanCloud; + +namespace RealtimeConsole { + public class LocalSignatureFactory : ILCIMSignatureFactory { + const string MasterKey = "pyvbNSh5jXsuFQ3C8EgnIdhw"; + + public LCIMSignature CreateConnectSignature(string clientId) { + long timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); + string nonce = NewNonce(); + string signature = GenerateSignature(LCApplication.AppId, clientId, string.Empty, timestamp.ToString(), nonce); + return new LCIMSignature { + Signature = signature, + Timestamp = timestamp, + Nonce = nonce + }; + } + + public LCIMSignature CreateStartConversationSignature(string clientId, IEnumerable memberIds) { + string sortedMemberIds = string.Empty; + if (memberIds != null) { + List sortedMemberList = memberIds.ToList(); + sortedMemberList.Sort(); + sortedMemberIds = string.Join(":", sortedMemberList); + } + long timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); + string nonce = NewNonce(); + string signature = GenerateSignature(LCApplication.AppId, clientId, sortedMemberIds, timestamp.ToString(), nonce); + return new LCIMSignature { + Signature = signature, + Timestamp = timestamp, + Nonce = nonce + }; + } + + public LCIMSignature CreateConversationSignature(string conversationId, string clientId, IEnumerable memberIds, string action) { + string sortedMemberIds = string.Empty; + if (memberIds != null) { + List sortedMemberList = memberIds.ToList(); + sortedMemberList.Sort(); + sortedMemberIds = string.Join(":", sortedMemberList); + } + long timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); + string nonce = NewNonce(); + string signature = GenerateSignature(LCApplication.AppId, clientId, conversationId, sortedMemberIds, timestamp.ToString(), nonce, action); + return new LCIMSignature { + Signature = signature, + Timestamp = timestamp, + Nonce = nonce + }; + } + + public LCIMSignature CreateBlacklistSignature(string conversationId, string clientId, IEnumerable memberIds, string action) { + string sortedMemberIds = string.Empty; + if (memberIds != null) { + List sortedMemberList = memberIds.ToList(); + sortedMemberList.Sort(); + sortedMemberIds = string.Join(":", sortedMemberList); + } + long timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); + string nonce = NewNonce(); + string signature = GenerateSignature(LCApplication.AppId, clientId, conversationId, sortedMemberIds, timestamp.ToString(), nonce, action); + return new LCIMSignature { + Signature = signature, + Timestamp = timestamp, + Nonce = nonce + }; + } + + private static string SignSHA1(string key, string text) { + HMACSHA1 hmac = new HMACSHA1(Encoding.UTF8.GetBytes(key)); + byte[] bytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(text)); + string signature = BitConverter.ToString(bytes).Replace("-", string.Empty); + return signature; + } + + private static string NewNonce() { + byte[] bytes = new byte[10]; + using (RandomNumberGenerator generator = RandomNumberGenerator.Create()) { + generator.GetBytes(bytes); + } + return Convert.ToBase64String(bytes); + } + + private static string GenerateSignature(params string[] args) { + string text = string.Join(":", args); + string signature = SignSHA1(MasterKey, text); + return signature; + } + } +}