diff --git a/projects/Infrastructure/Extensions/Base62Extensions.cs b/projects/Infrastructure/Extensions/Base62Extensions.cs new file mode 100644 index 00000000..10ae56c2 --- /dev/null +++ b/projects/Infrastructure/Extensions/Base62Extensions.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using System.Text; + +namespace Infrastructure.Extensions +{ + public static class Base62Extensions + { + private static string characterSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + public static string Base62Encode(this string value) + { + var arr = Encoding.UTF8.GetBytes(value); + return Base62Encode(arr); + } + + public static string Base62Decode(this string value) + { + var arr = new byte[value.Length]; + for (var i = 0; i < arr.Length; i++) + { + arr[i] = (byte)characterSet.IndexOf(value[i]); + } + + return Base62Decode(arr); + } + + public static string Base62Encode(this byte[] value) + { + var converted = BaseConvert(value, 256, 62); + var builder = new StringBuilder(); + for (var i = 0; i < converted.Length; i++) + { + builder.Append(characterSet[converted[i]]); + } + return builder.ToString(); + } + + public static string Base62Decode(this byte[] value) + { + var converted = BaseConvert(value, 62, 256); + + return Encoding.UTF8.GetString(converted, 0, converted.Length); + } + + private static byte[] BaseConvert(byte[] source, int sourceBase, int targetBase) + { + var result = new List(); + int count = 0; + while ((count = source.Length) > 0) + { + var quotient = new List(); + int remainder = 0; + for (var i = 0; i != count; i++) + { + int accumulator = source[i] + remainder * sourceBase; + byte digit = System.Convert.ToByte((accumulator - (accumulator % targetBase)) / targetBase); + remainder = accumulator % targetBase; + if (quotient.Count > 0 || digit != 0) + { + quotient.Add(digit); + } + } + + result.Insert(0, remainder); + source = quotient.ToArray(); + } + + var output = new byte[result.Count]; + for (int i = 0; i < result.Count; i++) + output[i] = (byte)result[i]; + + return output; + } + } +} \ No newline at end of file diff --git a/projects/Infrastructure/Extensions/DateTimeExtensions.cs b/projects/Infrastructure/Extensions/DateTimeExtensions.cs index af75fcb6..95890b4a 100644 --- a/projects/Infrastructure/Extensions/DateTimeExtensions.cs +++ b/projects/Infrastructure/Extensions/DateTimeExtensions.cs @@ -6,7 +6,7 @@ namespace Infrastructure.Extensions { public static long ToUnixTimeMilliseconds(this DateTime dateTime) { - return new DateTimeOffset().ToUnixTimeMilliseconds(); + return new DateTimeOffset(dateTime).ToUnixTimeMilliseconds(); } public static DateTime FromUnixTimeMilliseconds(this string timestrap) diff --git a/projects/Infrastructure/Extensions/HelperExtensions.cs b/projects/Infrastructure/Extensions/HelperExtensions.cs index 19d35c07..80f753cf 100644 --- a/projects/Infrastructure/Extensions/HelperExtensions.cs +++ b/projects/Infrastructure/Extensions/HelperExtensions.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -6,6 +7,8 @@ using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; using System.Text.RegularExpressions; namespace Infrastructure.Extensions diff --git a/projects/Infrastructure/Extensions/HexExtensions.cs b/projects/Infrastructure/Extensions/HexExtensions.cs index 83f51111..12ca589e 100644 --- a/projects/Infrastructure/Extensions/HexExtensions.cs +++ b/projects/Infrastructure/Extensions/HexExtensions.cs @@ -18,7 +18,9 @@ namespace Infrastructure.Extensions { var hex = new StringBuilder(bytes.Length * 2); foreach (byte b in bytes) + { hex.AppendFormat("{0:x2}", b); + } return hex.ToString(); } } diff --git a/projects/Infrastructure/Extensions/StringExtensions.cs b/projects/Infrastructure/Extensions/StringExtensions.cs index f3f4dfc5..3ebaad83 100644 --- a/projects/Infrastructure/Extensions/StringExtensions.cs +++ b/projects/Infrastructure/Extensions/StringExtensions.cs @@ -1,7 +1,8 @@ +using HtmlAgilityPack; using System; using System.Linq; +using System.Security.Cryptography; using System.Text; -using HtmlAgilityPack; namespace Infrastructure.Extensions { @@ -58,5 +59,53 @@ namespace Infrastructure.Extensions { return Enumerable.Range(0, hex.Length / 2).Select(x => Convert.ToByte(hex.Substring(x * 2, 2), 16)).ToArray(); } + + public static string DESEncrypt(this string value, string key) + { + using (var des = new DESCryptoServiceProvider()) + { + var inputByteArray = Encoding.Default.GetBytes(value); + var md5Key = key.Md5().Substring(0, 8); + des.Key = ASCIIEncoding.ASCII.GetBytes(md5Key); + des.IV = ASCIIEncoding.ASCII.GetBytes(md5Key); + using (var ms = new System.IO.MemoryStream()) + { + using (var cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write)) + { + cs.Write(inputByteArray, 0, inputByteArray.Length); + cs.FlushFinalBlock(); + var ret = new StringBuilder(); + foreach (var item in ms.ToArray()) + { + ret.AppendFormat("{0:X2}", item); + } + return ret.ToString(); + } + } + } + } + + public static string DESDecrypt(this string value, string key) + { + using (var des = new DESCryptoServiceProvider()) + { + var len = value.Length / 2; + byte[] inputByteArray = new byte[len]; + for (int i = 0; i < len; i++) + { + inputByteArray[i] = (byte)Convert.ToInt32(value.Substring(i * 2, 2), 16); + } + var md5Key = key.Md5().Substring(0, 8); + des.Key = ASCIIEncoding.ASCII.GetBytes(md5Key); + des.IV = ASCIIEncoding.ASCII.GetBytes(md5Key); + using (var ms = new System.IO.MemoryStream()) + { + CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write); + cs.Write(inputByteArray, 0, inputByteArray.Length); + cs.FlushFinalBlock(); + return Encoding.Default.GetString(ms.ToArray()); + } + } + } } } \ No newline at end of file diff --git a/projects/Infrastructure/Security/EncryptionService.cs b/projects/Infrastructure/Security/EncryptionService.cs index 58f8c942..e036436e 100644 --- a/projects/Infrastructure/Security/EncryptionService.cs +++ b/projects/Infrastructure/Security/EncryptionService.cs @@ -1,22 +1,19 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using System.Text; using Infrastructure.Extensions; using Microsoft.Extensions.Configuration; using Newtonsoft.Json; +using System; +using System.Security.Cryptography; +using System.Text; namespace Infrastructure.Security { public class EncryptionService : IEncryptionService { - private readonly string _key; - private readonly string _iv; + private readonly IConfiguration _configuration; public EncryptionService(IConfiguration configuration) { - this._key = configuration.GetSection("security").GetValue("key"); - this._iv = configuration.GetSection("security").GetValue("iv"); + this._configuration = configuration; } public string CreatePasswordHash(string password, string saltkey) @@ -52,35 +49,12 @@ namespace Infrastructure.Security public string EncryptObject(object obj) { - var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(obj)); - var key = Encoding.UTF8.GetBytes(this._key); - var iv = Encoding.UTF8.GetBytes(this._iv); - using (var ms = new MemoryStream()) - { - using (var cs = new CryptoStream(ms, TripleDES.Create().CreateEncryptor(key, iv), CryptoStreamMode.Write)) - { - cs.Write(data, 0, data.Length); - cs.FlushFinalBlock(); - } - return ms.ToArray().BytesToHex(); - } + return JsonConvert.SerializeObject(obj).DESEncrypt(this._configuration.GetSection("security").GetValue("key")); } public T DecryptObject(string value) { - var data = value.HexToBytes(); - var key = Encoding.UTF8.GetBytes(this._key); - var iv = Encoding.UTF8.GetBytes(this._iv); - using (var ms = new MemoryStream(data)) - { - using (var cs = new CryptoStream(ms, TripleDES.Create().CreateDecryptor(key, iv), CryptoStreamMode.Read)) - { - using (var sr = new StreamReader(cs, Encoding.UTF8)) - { - return JsonConvert.DeserializeObject(sr.ReadToEnd()); - } - } - } + return JsonConvert.DeserializeObject(value.DESDecrypt(this._configuration.GetSection("security").GetValue("key"))); } } } \ No newline at end of file diff --git a/projects/Infrastructure/Web/DeviceAttribute.cs b/projects/Infrastructure/Web/DeviceAttribute.cs index 1625836c..4345197d 100644 --- a/projects/Infrastructure/Web/DeviceAttribute.cs +++ b/projects/Infrastructure/Web/DeviceAttribute.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using System; namespace Infrastructure.Web { @@ -17,18 +18,60 @@ namespace Infrastructure.Web public override void OnResultExecuting(ResultExecutingContext context) { - var config = context.HttpContext.RequestServices.GetService(); - if (config["id"] != DeviceId.Md5().Base64UrlEncode().Md5()) + var result = true; + var message = ""; + var sn = DeviceId; + try { - if (!context.HttpContext.Request.Path.Value.StartsWith("/App/")) + var config = context.HttpContext.RequestServices.GetService(); + var hashCode = config["code"]; + var code = hashCode.DESDecrypt(sn); + var values = code.Split('-'); + if (sn != values[0]) { - context.Result = new RedirectResult("/Admin/Configuration"); + message = $"授权码不匹配当前设备{sn}"; + result = false; } else { - context.Result = new JsonResult($"设备{DeviceId}的授权码\"{config["id"]}\"异常"); + var timeSeconds = Convert.ToInt64(values[1]); + if (timeSeconds != 0) + { + var endTime = DateTimeOffset.FromUnixTimeSeconds(timeSeconds); + if (endTime < DateTimeOffset.UtcNow) + { + message = $"当前设备{sn}的授权码已过期"; + result = false; + } + } } } + catch (Exception ex) + { + ex.PrintStack(); + message = $"当前设备{sn}的授权码无效"; + result = false; + } + if (!result) + { + if (context.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest") + { + context.Result = new JsonResult(new { code = 1, message = message }); + } + else + { + var queryString = System.Web.HttpUtility.ParseQueryString(string.Empty); + queryString["message"] = message; + queryString["returnUrl"] = "/Admin/Configuration"; + var url = $"/Admin/Configuration/RedirectTo?{queryString.ToString()}"; + context.Result = new RedirectResult(url); + } + } + } + + private string GetCode(string deviceId) + { + return DeviceId.Base64UrlEncode().Md5(); } } } \ No newline at end of file diff --git a/projects/Infrastructure/Web/Mvc/BaseController.cs b/projects/Infrastructure/Web/Mvc/BaseController.cs index fb5ed4e3..db6a264e 100644 --- a/projects/Infrastructure/Web/Mvc/BaseController.cs +++ b/projects/Infrastructure/Web/Mvc/BaseController.cs @@ -11,7 +11,7 @@ namespace Infrastructure.Web public IActionResult RedirectTo(string action = "Index", string controller = null, string message = "操作成功,正在为您跳转", object routeValues = null, string returnUrl = null) { ViewBag.Message = message; - ViewBag.Url = string.IsNullOrEmpty(returnUrl) ? Url.Action(action, controller, routeValues) : returnUrl; + ViewBag.Url = string.IsNullOrEmpty(returnUrl) ? Url.Action("Index", controller, routeValues) : returnUrl; return View("Redirect"); } diff --git a/projects/IoT/IdGen/Program.cs b/projects/IoT/IdGen/Program.cs index d2a69bd8..bb03036b 100644 --- a/projects/IoT/IdGen/Program.cs +++ b/projects/IoT/IdGen/Program.cs @@ -7,20 +7,28 @@ namespace IdGen { private static void Main(string[] args) { - if (args.Length > 0) - { - Console.WriteLine(args[0].Md5().Base64UrlEncode().Md5()); - } + Console.WriteLine("物联网设备程序授权码生成工具:"); + var message = "请输入以空格分隔的设备编号、有效天数,有效天数为0则不限制使用期限"; + Console.WriteLine(message); while (true) { var input = Console.ReadLine(); - if (input == "q") + try { - break; + if (input == "q") + { + break; + } + var values = input.Split(' '); + var sn = values[0]; + var days = Convert.ToInt32(values[1]); + var endTime = days == 0 ? 0 : new DateTimeOffset(DateTime.UtcNow.Date.AddDays(days + 1).AddSeconds(-1)).ToUnixTimeMilliseconds(); + Console.WriteLine($"{sn}-{endTime}".DESEncrypt(sn)); } - else + catch (Exception ex) { - Console.WriteLine(input.Md5().Base64UrlEncode().Md5()); + Console.WriteLine(ex.Message); + Console.WriteLine(message); } } } diff --git a/projects/IoT/IoT.Shared/DeviceServices/FBee/FBeeService.cs b/projects/IoT/IoT.Shared/DeviceServices/FBee/FBeeService.cs index 324d4057..16fa3111 100644 --- a/projects/IoT/IoT.Shared/DeviceServices/FBee/FBeeService.cs +++ b/projects/IoT/IoT.Shared/DeviceServices/FBee/FBeeService.cs @@ -176,7 +176,7 @@ namespace IoT.Shared.DeviceServices.FBee ProductId = product.Id, NodeId = node.Id, }; - device.ConnectId = this._configuration["connectId"]; + device.ConnectId = this._configuration["sn"]; deviceRepo.Add(device); } device.Ip = ip; @@ -546,7 +546,7 @@ namespace IoT.Shared.DeviceServices.FBee } } device.IsOnline = isOnline != 0x00; - device.ConnectId = this._configuration["connectId"]; + device.ConnectId = this._configuration["sn"]; device.AddorUpdateData(device.CreateData(Keys.DeviceId, deviceId, DeviceDataType.Int, Keys.DeviceId, hidden: true)); device.AddorUpdateData(device.CreateData(Keys.Address, address, DeviceDataType.String, Keys.Address, hidden: true)); diff --git a/projects/IoT/IoT.Shared/DeviceServices/Onvif/OnvifService.cs b/projects/IoT/IoT.Shared/DeviceServices/Onvif/OnvifService.cs index e8118a20..5d023522 100644 --- a/projects/IoT/IoT.Shared/DeviceServices/Onvif/OnvifService.cs +++ b/projects/IoT/IoT.Shared/DeviceServices/Onvif/OnvifService.cs @@ -142,7 +142,7 @@ namespace IoT.Shared.DeviceServices.Onvif device.AddorUpdateData(device.CreateData("subrtmp", $"rtmp://{this._configuration["stream.rtmp"]}/live/sub{ipCamera.Id}", DeviceDataType.String, "子码流rtmp")); device.AddorUpdateData(device.CreateData("subflv", $"http://{this._configuration["stream.flv"]}/live/sub{ipCamera.Id}.flv", DeviceDataType.String, "子码流flv")); device.AddorUpdateData(device.CreateData("subhls", $"http://{this._configuration["stream.hls"]}/live/sub{ipCamera.Id}.m3u8", DeviceDataType.String, "子码流hls")); - device.ConnectId = this._configuration["connectId"]; + device.ConnectId = this._configuration["sn"]; deviceRepo.Add(device); deviceRepo.SaveChanges(); } diff --git a/projects/IoT/IoT.Shared/Infrastructure/BaseClientService.cs b/projects/IoT/IoT.Shared/Infrastructure/BaseClientService.cs index 51119842..d4ce2b2f 100644 --- a/projects/IoT/IoT.Shared/Infrastructure/BaseClientService.cs +++ b/projects/IoT/IoT.Shared/Infrastructure/BaseClientService.cs @@ -114,7 +114,7 @@ namespace IoT.Shared.Infrastructure private void InitConnection() { this._notifyHost = this._cfg["notify:host"]; - var url = $"http://{this._notifyHost}/hub?group={this._cfg["connectId"]}"; + var url = $"http://{this._notifyHost}/hub?group={this._cfg["sn"]}"; if (this.Connection != null) { this.Connection.DisposeAsync(); diff --git a/projects/IoT/IoT.Shared/Infrastructure/ClientService.cs b/projects/IoT/IoT.Shared/Infrastructure/ClientService.cs index 9147d4bf..7cfd6f1c 100644 --- a/projects/IoT/IoT.Shared/Infrastructure/ClientService.cs +++ b/projects/IoT/IoT.Shared/Infrastructure/ClientService.cs @@ -81,7 +81,7 @@ namespace IoT.Shared.Infrastructure { if (method == Methods.HealthCheckRequest) { - this.ClientToServer(Methods.HealthCheckResponse, this._cfg["node.number"]); + this.ClientToServer(Methods.HealthCheckResponse, this._cfg["sn"]); } else if (method == Methods.GetProductRequest) { @@ -103,7 +103,7 @@ namespace IoT.Shared.Infrastructure var url = $"http://localhost:{port}{message}"; var httpClient = scope.ServiceProvider.GetService().CreateClient(); var result = httpClient.GetStringAsync(url).Result; - this.Connection.SendAsync(Methods.ClientToServer, Methods.ApiCallback, result, cfg["connectId"]); + this.Connection.SendAsync(Methods.ClientToServer, Methods.ApiCallback, result, cfg["sn"]); } else if (method == Methods.CallScene) { diff --git a/projects/IoT/IoTNode/Program.cs b/projects/IoT/IoTNode/Program.cs index 65119e14..edd71471 100644 --- a/projects/IoT/IoTNode/Program.cs +++ b/projects/IoT/IoTNode/Program.cs @@ -15,14 +15,15 @@ namespace IoTNode Console.OutputEncoding = System.Text.Encoding.UTF8; var host = "localhost"; var stream = "192.168.3.124"; + var cpuNumber = Helper.Instance.GetCPUNumber(); WebHost.CreateDefaultBuilder(args) .Run(new List { - new EFConfigurationValue { Id = "id", Value= "根据设备编号生成的授权码" }, + new EFConfigurationValue { Id = "sn", Value= cpuNumber }, + new EFConfigurationValue { Id = "code", Value= "根据设备编号生成的授权码" }, new EFConfigurationValue { Id = "openapi.name", Value= "v1" }, new EFConfigurationValue { Id = "openapi.title", Value= "fbee api" }, new EFConfigurationValue { Id = "openapi.version", Value= "1.0" }, new EFConfigurationValue { Id = "security:key", Value= "111111111111111111111111"}, - new EFConfigurationValue { Id = "security:iv", Value= "11111111"}, new EFConfigurationValue { Id = "jwt:key", Value= "111111111111111111111111"}, new EFConfigurationValue { Id = "email:host", Value= "nbaxp.com"}, new EFConfigurationValue { Id = "email:port", Value= "25"}, @@ -33,8 +34,6 @@ namespace IoTNode new EFConfigurationValue { Id = "notify:enabled", Value= "true"}, new EFConfigurationValue { Id = "notify:host", Value= $"{host}:8001"}, new EFConfigurationValue { Id = "timer.seconds", Value="60"}, - new EFConfigurationValue { Id = "connectId", Value= Helper.Instance.GetCPUNumber() }, - new EFConfigurationValue { Id = "node.number", Value= Helper.Instance.GetCPUNumber() }, new EFConfigurationValue { Id = "influxdb:url", Value= "http://localhost:8086"}, new EFConfigurationValue { Id = "influxdb:usr", Value= "admin"}, new EFConfigurationValue { Id = "influxdb:pwd", Value= "admin"}, diff --git a/projects/IoTCenter/Controllers/HomeController.cs b/projects/IoTCenter/Controllers/HomeController.cs index 168fcb7c..292f77e0 100644 --- a/projects/IoTCenter/Controllers/HomeController.cs +++ b/projects/IoTCenter/Controllers/HomeController.cs @@ -2,6 +2,7 @@ using Application.Domain.Entities; using Infrastructure.Data; using Infrastructure.Domain; using Infrastructure.Extensions; +using Infrastructure.Web; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -42,7 +43,7 @@ namespace IoTCenter.Controllers this._eventPublisher = eventPublisher; } - [Authorize] + [Authorize, Device] public IActionResult Index() { //this._eventPublisher.Publish(new EntityInsertedEvent(new Node())); diff --git a/projects/IoTCenter/Program.cs b/projects/IoTCenter/Program.cs index 32a08807..17f72eeb 100644 --- a/projects/IoTCenter/Program.cs +++ b/projects/IoTCenter/Program.cs @@ -1,5 +1,6 @@ using Infrastructure.Application; using Infrastructure.Configuration; +using Infrastructure.Extensions; using Infrastructure.Web.Hosting; using Microsoft.AspNetCore; using System; @@ -13,14 +14,16 @@ namespace IoTCenter { Console.OutputEncoding = System.Text.Encoding.UTF8; var host = "localhost"; + var cpuNumber = Helper.Instance.GetCPUNumber(); WebHost.CreateDefaultBuilder(args) .Run(new List { + new EFConfigurationValue { Id = "sn", Value= cpuNumber }, + new EFConfigurationValue { Id = "code", Value= "根据设备编号生成的授权码" }, new EFConfigurationValue { Id = "openapi.name", Value= "v1" }, new EFConfigurationValue { Id = "openapi.title", Value= "web api" }, new EFConfigurationValue { Id = "openapi.version", Value= "1.0" }, new EFConfigurationValue { Id = "server.urls", Value= "http://*:8001" }, new EFConfigurationValue { Id = "security:key", Value= "111111111111111111111111"}, - new EFConfigurationValue { Id = "security:iv", Value= "11111111"}, new EFConfigurationValue { Id = "jwt:key", Value= "111111111111111111111111"}, new EFConfigurationValue { Id = "usercenter:key", Value= "123456"}, new EFConfigurationValue { Id = "usercenter:login", Value= $"http://{host}:8000/Account/Login"}, diff --git a/projects/UserCenter/Program.cs b/projects/UserCenter/Program.cs index 69baf935..d98dd7de 100644 --- a/projects/UserCenter/Program.cs +++ b/projects/UserCenter/Program.cs @@ -17,7 +17,6 @@ namespace UserCenter new EFConfigurationValue { Id = "openapi.version", Value= "1.0" }, new EFConfigurationValue { Id = "server.urls", Value= "http://*:8000" }, new EFConfigurationValue { Id = "security:key", Value= "111111111111111111111111"}, - new EFConfigurationValue { Id = "security:iv", Value= "11111111"}, new EFConfigurationValue { Id = "jwt:key", Value= "111111111111111111111111"}, new EFConfigurationValue { Id = "email:host", Value= "nbaxp.com"}, new EFConfigurationValue { Id = "email:port", Value= "25"},