diff --git a/AppShell.xaml.cs b/AppShell.xaml.cs index cfbca3e..b0c61c3 100644 --- a/AppShell.xaml.cs +++ b/AppShell.xaml.cs @@ -22,7 +22,9 @@ namespace SunlightAggregationTerminal await Navigation.PushModalAsync(new NavigationPage(logpage)); } else - {//发送登录请求 + { + + //发送登录请求 if (await AppModels.LogIn()) {//拒绝登陆时打开登录页面 var logpage = new View.LogPage(); diff --git a/Class/SecureJsonService.cs b/Class/SecureJsonService.cs new file mode 100644 index 0000000..c2919ad --- /dev/null +++ b/Class/SecureJsonService.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.IO.Compression; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; + +namespace SunlightAggregationTerminal.Class +{ + public class SecureJsonService + { + private readonly byte[] _aesKey = Encoding.UTF8.GetBytes("12345678901234567890123456789012"); + + /// + /// 调用一:发送端处理 + /// 流程:JSON序列化 -> GZip压缩 -> AES加密 -> Base64编码 + /// + public string CompressEncryptAndSend(object dataObject) + { + try + { + // 1. JSON 序列化 (使用 System.Text.Json 进行最小化处理) + // JsonSerializerOptions 默认不包含缩进,即最小化 + string jsonString = JsonSerializer.Serialize(dataObject); + byte[] jsonBytes = Encoding.UTF8.GetBytes(jsonString); + + // 2. GZip 压缩 + byte[] compressedBytes = CompressBytes(jsonBytes); + + // 3. AES 加密 + byte[] encryptedBytes = EncryptBytes(compressedBytes); + + // 4. Base64 编码 (转为字符串以便在网络传输) + string finalPayload = Convert.ToBase64String(encryptedBytes); + + return finalPayload; + } + catch (Exception ex) + { + Console.WriteLine($"发送端处理错误: {ex.Message}"); + throw; + } + } + + /// + /// 调用二:接收端处理 + /// 流程:Base64解码 -> AES解密 -> GZip解压 -> JSON反序列化 + /// + public T? ReceiveDecryptAndDecompress(string payload) + { + try + { + // 1. Base64 解码 + byte[] encryptedBytes = Convert.FromBase64String(payload); + + // 2. AES 解密 + byte[] compressedBytes = DecryptBytes(encryptedBytes); + + // 3. GZip 解压 + byte[] jsonBytes = DecompressBytes(compressedBytes); + + // 4. JSON 反序列化 + string jsonString = Encoding.UTF8.GetString(jsonBytes); + T result = JsonSerializer.Deserialize(jsonString); + + return result; + } + catch (Exception) + { + throw; + } + } + + // --- 底层辅助方法 --- + + private byte[] CompressBytes(byte[] data) + { + using (var outputStream = new MemoryStream()) + { + using (var gzipStream = new GZipStream(outputStream, CompressionLevel.Optimal)) + { + gzipStream.Write(data, 0, data.Length); + } + return outputStream.ToArray(); + } + } + + private byte[] DecompressBytes(byte[] data) + { + using (var inputStream = new MemoryStream(data)) + { + using (var gzipStream = new GZipStream(inputStream, CompressionMode.Decompress)) + { + using (var outputStream = new MemoryStream()) + { + gzipStream.CopyTo(outputStream); + return outputStream.ToArray(); + } + } + } + } + + private byte[] EncryptBytes(byte[] data) + { + using (Aes aes = Aes.Create()) + { + aes.Key = _aesKey; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.PKCS7; + aes.GenerateIV(); // 每次加密生成随机IV + + var encryptor = aes.CreateEncryptor(aes.Key, aes.IV); + + using (var ms = new MemoryStream()) + { + // 重要:先将 IV 写入流中,以便解密时读取 + ms.Write(aes.IV, 0, aes.IV.Length); + + using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) + { + cs.Write(data, 0, data.Length); + cs.FlushFinalBlock(); + return ms.ToArray(); + } + } + } + } + + private byte[] DecryptBytes(byte[] data) + { + using (Aes aes = Aes.Create()) + { + aes.Key = _aesKey; + aes.Mode = CipherMode.CBC; + aes.Padding = PaddingMode.PKCS7; + + // 从数据开头读取 IV (AES IV 固定为 16 字节) + byte[] iv = new byte[16]; + Buffer.BlockCopy(data, 0, iv, 0, iv.Length); + aes.IV = iv; + + var decryptor = aes.CreateDecryptor(aes.Key, aes.IV); + + using (var ms = new MemoryStream(data, iv.Length, data.Length - iv.Length)) + { + using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) + { + using (var resultStream = new MemoryStream()) + { + cs.CopyTo(resultStream); + return resultStream.ToArray(); + } + } + } + } + } + } +} diff --git a/Class/TcpServer.cs b/Class/TcpServer.cs index fbe4105..ddab080 100644 --- a/Class/TcpServer.cs +++ b/Class/TcpServer.cs @@ -21,6 +21,10 @@ namespace SunlightAggregationTerminal.Class ServerName = Name; ServerPassword = Password; ServerTerminal = Terminal; + + + _= TcpClient(tcpClient, ServerIP, "7789"); + } public static async void Query(string Dat) { @@ -30,10 +34,8 @@ namespace SunlightAggregationTerminal.Class } - private static async Task CreateCustomService() + public static async Task TcpClient(TcpClient tcpClient, string ip, string port) { - tcpClient.Config.SetRemoteIPHost(ServerIP); - tcpClient.Connecting = (client, e) => { return EasyTask.CompletedTask; };//即将连接到服务器,此时已经创建socket,但是还未建立tcp tcpClient.Connected = (client, e) => { return EasyTask.CompletedTask; };//成功连接到服务器 tcpClient.Closing = (client, e) => { return EasyTask.CompletedTask; };//即将从服务器断开连接。此处仅主动断开才有效。 @@ -44,10 +46,22 @@ namespace SunlightAggregationTerminal.Class //从服务器收到信息。但是一般byteBlock和requestInfo会根据适配器呈现不同的值。 var mes = e.Memory.Span.ToString(Encoding.UTF8); tcpClient.Logger.Info($"客户端接收到信息:{mes}"); + + return EasyTask.CompletedTask; }; #endregion + await tcpClient.SetupAsync(new TouchSocketConfig() + .SetRemoteIPHost(ip + ":" + port) + .ConfigurePlugins(a => + { + }) + .ConfigureContainer(a => + { + + } + )); await tcpClient.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。 } } diff --git a/Models/AppModels.cs b/Models/AppModels.cs index 174ee16..c8b12cc 100644 --- a/Models/AppModels.cs +++ b/Models/AppModels.cs @@ -39,6 +39,9 @@ namespace SunlightAggregationTerminal.Models App.GlobalData.MessageNotificationMode = Convert.ToBoolean(dat.Field("MessageNotificationMode")); TcpServer.Configuration(App.GlobalData.ServerID, App.GlobalData.User, App.GlobalData.UserPassword, dat.Field("GUID") ?? "Not"); + + //启动tcp + // TcpServer.CreateCustomService(); } } } diff --git a/View/LogPage.xaml b/View/LogPage.xaml index 5a2c1b7..91693db 100644 --- a/View/LogPage.xaml +++ b/View/LogPage.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SunlightAggregationTerminal.View.LogPage" Title="登录"> + + + + + + + \ No newline at end of file diff --git a/View/LogPage.xaml.cs b/View/LogPage.xaml.cs index 65d0292..214b0ec 100644 --- a/View/LogPage.xaml.cs +++ b/View/LogPage.xaml.cs @@ -70,10 +70,12 @@ public partial class LogPage : ContentPage private async void Log_Clicked(object sender, EventArgs e)//登录 { - TcpServer.Configuration(SERVERIP.Text, UsernameEntry.Text, PasswordEntry.Text, + TcpServer.Configuration(SERVERIP.Text, UsernameEntry.Text, PasswordEntry.Text, Models.AppModels.GetAppUniqueId()); - if (await AppModels.LogIn()) + LoadingIndicator.IsVisible = true; + + if (await AppModels.LogIn()) { var temp_name = AppModels.Select("SELECT * FROM Users WHERE User='" + UsernameEntry.Text + "' LIMIT 1;")!.Tables[0]; if (temp_name != null) @@ -104,8 +106,14 @@ public partial class LogPage : ContentPage App.GlobalData.ServerID + "');"; AppModels.INSERT(sql); } + + await Navigation.PopModalAsync(); + } + else + { + await DisplayAlertAsync("未登录", "服务器不存在或拒绝访问", "是"); } - await Navigation.PopModalAsync(); + LoadingIndicator.IsVisible = false; }