IndexService.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  1. using IES.ExamServer.Helpers;
  2. using Microsoft.Extensions.Caching.Memory;
  3. using System.Diagnostics;
  4. using System.Net.NetworkInformation;
  5. using System.Net;
  6. using System.Runtime.InteropServices;
  7. using System.Text.Json.Nodes;
  8. using System.Text.RegularExpressions;
  9. using IES.ExamServer.Models;
  10. using System.Text;
  11. using IES.ExamServer.DI;
  12. using IES.ExamServer.Helper;
  13. using System;
  14. using System.Security.Principal;
  15. namespace IES.ExamServer.Services
  16. {
  17. public static class IndexService
  18. {
  19. /// <summary>
  20. /// 修改IP域名映射,以及处理证书是否安装的问题。
  21. /// </summary>
  22. /// <param name="ip"></param>
  23. /// <param name="_memoryCache"></param>
  24. /// <param name="_liteDBFactory"></param>
  25. /// <param name="connectionService"></param>
  26. /// <returns></returns>
  27. public static async Task<(int code, int code_cer ,int code_hosts,int code_zip, string msg,string hosts_msg, string cer_msg,string zip_msg,Network? primaryNetwork)> ModifyHosts(string? ip,IMemoryCache _memoryCache,LiteDBFactory _liteDBFactory,CenterServiceConnectionService connectionService)
  28. {
  29. Network? primaryNetwork = null;
  30. int code = 0, code_cer = 0,code_hosts=0,code_zip=0 ;
  31. string hosts_msg = string.Empty, cer_msg = string.Empty, zip_msg = string.Empty;
  32. StringBuilder sb = new StringBuilder();
  33. try
  34. {
  35. (string? hostsIp, string hostsMsg) = SystemScriptHelper.FindIpAddressForDomain("exam.habook.local");
  36. string batscriptPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "batscript");
  37. if (!Directory.Exists(batscriptPath))
  38. {
  39. Directory.CreateDirectory(batscriptPath);
  40. }
  41. string pathCerNew = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "batscript", "certificate.cer");
  42. string pathBatNew = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "batscript", "install_certificate.bat");
  43. if (!System.IO.File.Exists(pathCerNew) || !System.IO.File.Exists(pathBatNew))
  44. {
  45. string pathCer = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "cer", "certificate.cer");
  46. System.IO.File.Copy(pathCer, pathCerNew);
  47. string pathBat = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "cer", "install_certificate.bat");
  48. System.IO.File.Copy(pathBat, pathBatNew);
  49. }
  50. var needInstall = SystemScriptHelper.CheckCertificate(pathCerNew);
  51. if (!needInstall)
  52. {
  53. if (SystemScriptHelper.IsAdministrator())
  54. {
  55. var res = ProcessHelper.ExecuteProcess(pathBatNew);
  56. sb.Append(res.msg);
  57. code_cer = res.code;
  58. switch (res.code)
  59. {
  60. case 200:
  61. cer_msg = "证书安装成功";
  62. break;
  63. case 1:
  64. cer_msg = $"证书安装异常:{res.msg}";
  65. break;
  66. case 2:
  67. cer_msg = $"证书脚本运行异常:{res.msg}";
  68. break;
  69. case 500:
  70. cer_msg = $"证书代码执行异常:{res.msg}";
  71. break;
  72. }
  73. }
  74. else
  75. {
  76. code_cer = 401;
  77. sb.Append("请使用管理员身份运行本程序,如果已经安装过脚本请忽略!");
  78. cer_msg = $"请使用管理员身份安装证书!";
  79. }
  80. }
  81. else
  82. {
  83. code_cer = 200;
  84. cer_msg = $"证书已经安装!";
  85. }
  86. //获取主站配置信息。
  87. ServerDevice serverDevice = _memoryCache.Get<ServerDevice>(Constant._KeyServerDevice);
  88. var primaryNetworks= _liteDBFactory.GetLiteDatabase().GetCollection<Network>().FindAll().ToList();
  89. //传入的ip为不为空,切换
  90. if (!string.IsNullOrWhiteSpace(ip) )
  91. {
  92. if (serverDevice != null && serverDevice.networks.IsNotEmpty())
  93. {
  94. primaryNetwork = serverDevice.networks.FindAll(x => ip.Equals(x.ip))?.FirstOrDefault();
  95. }
  96. }
  97. else
  98. {
  99. if (serverDevice != null && serverDevice.networks.IsNotEmpty())
  100. {
  101. //传入的ip为空,则尝试加载 已经配置过的主站域名ip
  102. if (primaryNetworks.IsNotEmpty())
  103. {
  104. foreach (var network in primaryNetworks)
  105. {
  106. var exists = serverDevice.networks.FindAll(x => x.id!.Equals(network.id));
  107. if (exists.IsNotEmpty())
  108. {
  109. if (primaryNetwork == null)
  110. {
  111. primaryNetwork = network;
  112. }
  113. }
  114. }
  115. }
  116. //没有找到主站设置信息,则尝试赋值物理网卡配置信息
  117. if (primaryNetwork == null)
  118. {
  119. primaryNetwork = serverDevice.networks.FirstOrDefault();//第一个是物理网卡
  120. }
  121. }
  122. }
  123. if (primaryNetwork != null)
  124. {
  125. string pathBatHosts = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "cer", "modify_hosts.bat");
  126. string text = await System.IO.File.ReadAllTextAsync(pathBatHosts);
  127. // 使用正则表达式替换 IP 地址
  128. string pattern = @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b";
  129. string result = Regex.Replace(text, pattern, primaryNetwork.ip!);
  130. string pathBatHostsNew = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "batscript", "modify_hosts.bat");
  131. await System.IO.File.WriteAllTextAsync(pathBatHostsNew, result);
  132. string pathBatStudent = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "cer", "student_manual.bat");
  133. string textStudent = await System.IO.File.ReadAllTextAsync(pathBatStudent);
  134. // 使用正则表达式替换 IP 地址
  135. string resultStudent = Regex.Replace(textStudent, pattern, primaryNetwork.ip!);
  136. string pathBatStudentNew = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "batscript", "student_manual.bat");
  137. await System.IO.File.WriteAllTextAsync(pathBatStudentNew, resultStudent);
  138. if (string.IsNullOrWhiteSpace(hostsIp) || !hostsIp.Equals(primaryNetwork.ip))
  139. {
  140. if (SystemScriptHelper.IsAdministrator())
  141. {
  142. var resHosts = ProcessHelper.ExecuteProcess(pathBatHostsNew);
  143. sb.Append(resHosts.msg);
  144. code_hosts = resHosts.code;
  145. switch (resHosts.code)
  146. {
  147. case 200:
  148. hosts_msg = "IP域名映射成功";
  149. break;
  150. case 1:
  151. hosts_msg = $"IP域名映射异常:{resHosts.msg}";
  152. break;
  153. case 2:
  154. hosts_msg = $"IP域名映射脚本运行异常:{resHosts.msg}";
  155. break;
  156. case 500:
  157. hosts_msg = $"IP域名映射代码执行异常:{resHosts.msg}";
  158. break;
  159. }
  160. }
  161. else
  162. {
  163. code_hosts = 401;
  164. sb.Append("请使用管理员身份执行本程序!");
  165. hosts_msg = $"请使用管理员身份映射IP域名!";
  166. }
  167. }
  168. else
  169. {
  170. code_hosts = 200;
  171. sb.Append("IP域名映射已存在,无需再次映射!");
  172. hosts_msg = $"IP域名映射已存在,无需再次映射!";
  173. }
  174. string scriptPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "script");
  175. if (!Directory.Exists(scriptPath))
  176. {
  177. Directory.CreateDirectory(scriptPath);
  178. }
  179. string batscriptZipPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "script", "student_script.zip");
  180. var res = ZipHelper.CreateZip(batscriptPath, batscriptZipPath);
  181. if (res.res)
  182. {
  183. code_zip = 200;
  184. sb.Append(res.msg);
  185. zip_msg = res.msg;
  186. }
  187. else
  188. {
  189. code_zip = 400;
  190. sb.Append("脚本文件创建异常!");
  191. zip_msg = res.msg;
  192. }
  193. serverDevice!.networks.ForEach(x =>
  194. {
  195. x.primary = 0;
  196. x.batscriptZip = null;
  197. if (x.id!.Equals(primaryNetwork.id))
  198. {
  199. x.primary = code_hosts == 200 ? 1 : 0;
  200. x.batscriptZip = res.res ? "script/student_script.zip" : null;
  201. }
  202. });
  203. //更新设备的主站设备信息
  204. connectionService.serverDevice = serverDevice;
  205. connectionService.primaryNetwork = primaryNetwork;
  206. _memoryCache.Set<ServerDevice>(Constant._KeyServerDevice, serverDevice);
  207. _liteDBFactory.GetLiteDatabase().GetCollection<ServerDevice>().Upsert(serverDevice);
  208. //清理后再保存,保证只有一条主站数据。
  209. _liteDBFactory.GetLiteDatabase().GetCollection<Network>().DeleteAll();
  210. _liteDBFactory.GetLiteDatabase().GetCollection<Network>().Upsert(primaryNetwork);
  211. //所有执行成功
  212. code = 200;
  213. sb.Append("证书安装成功,域名IP绑定成功,脚本文件创建成功!");
  214. }
  215. else {
  216. hosts_msg = $"未匹配到可用网卡和IP";
  217. code_hosts = 400;
  218. }
  219. }
  220. catch (Exception ex)
  221. {
  222. code = 500;
  223. Console.WriteLine($"ip bind error。{ex.Message},{ex.StackTrace}");
  224. return (500,code_cer,code_hosts,code_zip, $"域名IP绑定错误,{ex.Message},{ex.StackTrace}",hosts_msg,cer_msg,zip_msg, primaryNetwork);
  225. }
  226. return (code, code_cer, code_hosts, code_zip, sb.ToString(), hosts_msg, cer_msg, zip_msg, primaryNetwork);
  227. }
  228. public static async Task<(string content_response, HttpStatusCode code)> GetMusicServer_thirdLocalCacheConfig(IHttpClientFactory httpClientFactory, CenterServiceConnectionService _connectionService , string requestBody)
  229. {
  230. string content_response = string.Empty;
  231. HttpStatusCode code = HttpStatusCode.OK ;
  232. try {
  233. string url = _connectionService!.musicUrl!;
  234. var client = httpClientFactory.CreateClient();
  235. //client.DefaultRequestHeaders.Add("Accept", "application/json, text/plain, */*");
  236. client.DefaultRequestHeaders.Add("appId", Constant._MusicAIServerAppId);
  237. client.DefaultRequestHeaders.Add("encrypt", "1");
  238. client.DefaultRequestHeaders.Add("schoolCode", _connectionService.serverDevice?.school?.id);
  239. var content = new StringContent(requestBody, Encoding.UTF8, "application/json");
  240. // 发送 PUT 请求
  241. HttpResponseMessage response = await client.PutAsync($"{url}/musicLocalCache/thirdLocalCacheConfig", content);
  242. // 处理响应
  243. if (response.IsSuccessStatusCode)
  244. {
  245. content_response = await response.Content.ReadAsStringAsync();
  246. //Console.WriteLine("Response: " + responseData);
  247. }
  248. else
  249. {
  250. // Console.WriteLine("Error: " + response.StatusCode);
  251. code=response.StatusCode;
  252. content_response = await response.Content.ReadAsStringAsync();
  253. }
  254. } catch (Exception ex) {
  255. code= HttpStatusCode.InternalServerError;
  256. content_response = ex.Message;
  257. }
  258. return (content_response, code);
  259. }
  260. public static ServerDevice GetServerDevice( string remote,string region)
  261. {
  262. string hostName = $"{Environment.UserName}-{Dns.GetHostName()}";
  263. string os = RuntimeInformation.OSDescription;
  264. //获取当前客户端的服务端口
  265. string currentUserName = Environment.UserName;
  266. string MachineName =Environment.MachineName;
  267. //var s = Environment;
  268. ServerDevice device = new ServerDevice { name =hostName, userName=currentUserName, os= os,region=region,remote=remote };
  269. int CpuCoreCount = 0;
  270. long MenemorySize = 0;
  271. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  272. {
  273. CpuCoreCount = Environment.ProcessorCount;
  274. MenemorySize = MemoryInfo.GetTotalPhysicalMemory();
  275. //using (PerformanceCounter pc = new PerformanceCounter("Memory", "Available Bytes"))
  276. //{
  277. // // 获取可用内存
  278. // AvailableMemory = (long)pc.NextValue();
  279. //}
  280. //using (PerformanceCounter pc = new PerformanceCounter("Memory", "Committed Bytes"))
  281. //{
  282. // // 获取可用内存
  283. // CommittedMemory= (long)pc.NextValue();
  284. //}
  285. //MenemorySize=AvailableMemory+CommittedMemory;
  286. //
  287. // 获取CPU核心数
  288. //int processorCount = Environment.ProcessorCount;
  289. //Console.WriteLine("CPU 核心数: " + processorCount);
  290. //using (ManagementClass managementClass = new ManagementClass("Win32_Processor"))
  291. //{
  292. // using (ManagementObjectCollection managementObjectCollection = managementClass.GetInstances())
  293. // {
  294. // foreach (ManagementObject managementObject in managementObjectCollection)
  295. // {
  296. // CpuCoreCount += Convert.ToInt32(managementObject.Properties["NumberOfLogicalProcessors"].Value);
  297. // }
  298. // }
  299. //}
  300. //using (ManagementObjectSearcher searcher1 = new ManagementObjectSearcher("SELECT TotalPhysicalMemory FROM Win32_ComputerSystem"))
  301. //{
  302. // foreach (ManagementObject obj in searcher1.Get())
  303. // {
  304. // long MenemorySizes1 = Convert.ToInt64(obj["TotalPhysicalMemory"]);
  305. // break; // 只需要第一个结果
  306. // }
  307. //}
  308. //using (ManagementClass mc = new ManagementClass("Win32_ComputerSystem"))
  309. //{
  310. // using (ManagementObjectCollection moc = mc.GetInstances())
  311. // {
  312. // foreach (ManagementObject mo in moc)
  313. // {
  314. // if (mo["TotalPhysicalMemory"]!= null)
  315. // {
  316. // MenemorySize = Convert.ToInt64(mo["TotalPhysicalMemory"]);
  317. // }
  318. // }
  319. // }
  320. //}
  321. if (Environment.Is64BitOperatingSystem)
  322. {
  323. device.bit="64";
  324. }
  325. else
  326. {
  327. device.bit="32";
  328. }
  329. //ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Name, MaxClockSpeed FROM Win32_Processor");
  330. //foreach (ManagementObject mo in searcher.Get())
  331. //{
  332. // string? cpuName = mo["Name"].ToString();
  333. // string? clockSpeed = mo["MaxClockSpeed"].ToString();
  334. // //Console.WriteLine($"CPU 名称: {cpuName}");
  335. // //Console.WriteLine($"CPU 主频: {clockSpeed} MHz");
  336. // device.cpuInfos.Add(new CPUInfo { name = cpuName, hz = clockSpeed });
  337. //}
  338. string cpuName = MemoryInfo.GetCpuName();
  339. var clockSpeed = MemoryInfo.GetCpuSpeed();
  340. device.cpuInfos.Add(new CPUInfo { name = cpuName, hz = $"{clockSpeed}" });
  341. }
  342. else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
  343. {
  344. //int processorCount = Environment.ProcessorCount;
  345. // Console.WriteLine("CPU 核心数: " + processorCount);
  346. try {
  347. string cpuInfo = File.ReadAllText("/proc/cpuinfo");
  348. string[] cpu_lines = cpuInfo.Split('\n');
  349. CpuCoreCount= cpu_lines.Count(line => line.StartsWith("processor", StringComparison.OrdinalIgnoreCase));
  350. string? cpuNameLine = cpuInfo.Split('\n').FirstOrDefault(line => line.StartsWith("model name"));
  351. string? clockSpeedLine = cpuInfo.Split('\n').FirstOrDefault(line => line.StartsWith("cpu MHz"));
  352. string cpuName = string.Empty;
  353. string clockSpeed = string.Empty;
  354. if (cpuNameLine!= null)
  355. {
  356. cpuName = cpuNameLine.Split(':').Last().Trim();
  357. }
  358. if (clockSpeedLine!= null)
  359. {
  360. clockSpeed = clockSpeedLine.Split(':').Last().Trim();
  361. }
  362. device.cpuInfos.Add(new CPUInfo { name = cpuName, hz = clockSpeed });
  363. } catch (Exception ex)
  364. {
  365. }
  366. string[] mem_lines = File.ReadAllLines("/proc/meminfo");
  367. var match = mem_lines.FirstOrDefault(line => line.StartsWith("MemTotal:"));
  368. if (match != null)
  369. {
  370. var matchResult = Regex.Match(match, @"\d+");
  371. if (matchResult.Success)
  372. {
  373. MenemorySize= long.Parse(matchResult.Value);
  374. }
  375. }
  376. }
  377. else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
  378. {
  379. try {
  380. using (var process = new Process())
  381. {
  382. process.StartInfo.FileName = "/usr/sbin/sysctl";
  383. process.StartInfo.Arguments = "-n hw.ncpu";
  384. process.StartInfo.RedirectStandardOutput = true;
  385. process.StartInfo.UseShellExecute = false;
  386. process.Start();
  387. string output = process.StandardOutput.ReadToEnd().Trim();
  388. int coreCount;
  389. if (int.TryParse(output, out coreCount))
  390. {
  391. CpuCoreCount= coreCount;
  392. }
  393. }
  394. }
  395. catch (Exception ex) { }
  396. try
  397. {
  398. using (var process = new Process())
  399. {
  400. process.StartInfo.FileName = "/usr/sbin/sysctl";
  401. process.StartInfo.Arguments = "-n hw.memsize";
  402. process.StartInfo.RedirectStandardOutput = true;
  403. process.StartInfo.UseShellExecute = false;
  404. process.Start();
  405. string output = process.StandardOutput.ReadToEnd().Trim();
  406. long memorySize;
  407. if (long.TryParse(output, out memorySize))
  408. {
  409. MenemorySize= memorySize;
  410. }
  411. }
  412. }
  413. catch (Exception ex) { }
  414. try
  415. {
  416. using (var process = new Process())
  417. {
  418. process.StartInfo.FileName = "/usr/sbin/sysctl";
  419. process.StartInfo.Arguments = "-n machdep.cpu.brand_string";
  420. process.StartInfo.RedirectStandardOutput = true;
  421. process.StartInfo.UseShellExecute = false;
  422. process.Start();
  423. string cpuName = process.StandardOutput.ReadToEnd().Trim();
  424. process.StartInfo.FileName = "/usr/sbin/sysctl";
  425. process.StartInfo.Arguments = "-n hw.cpu.frequency";
  426. process.StartInfo.RedirectStandardOutput = true;
  427. process.StartInfo.UseShellExecute = false;
  428. process.Start();
  429. string clockSpeed = process.StandardOutput.ReadToEnd().Trim();
  430. //Console.WriteLine($"CPU 名称: {cpuName}");
  431. //Console.WriteLine($"CPU 主频: {clockSpeed} Hz");
  432. device.cpuInfos.Add(new CPUInfo { name = cpuName, hz = clockSpeed });
  433. }
  434. }
  435. catch (Exception ex)
  436. {
  437. Console.WriteLine($"出现错误: {ex.Message}");
  438. }
  439. if (Environment.Is64BitOperatingSystem)
  440. {
  441. device.bit="64";
  442. }
  443. else
  444. {
  445. device.bit="32";
  446. }
  447. }
  448. if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
  449. {
  450. device.arch="ARM64";
  451. }
  452. else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm)
  453. {
  454. device.arch="ARM32";
  455. }
  456. else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
  457. {
  458. device.arch="X64";
  459. }
  460. else if (RuntimeInformation.ProcessArchitecture == Architecture.X86)
  461. {
  462. device.arch="X86";
  463. }
  464. else
  465. {
  466. device.arch=$"未知({device.arch})";
  467. }
  468. //Console.WriteLine("CPU 核心数: " + CpuCoreCount+",RAM 大小:"+MenemorySize);
  469. device.cpu=CpuCoreCount;
  470. device.ram=MenemorySize;
  471. var nics = NetworkInterface.GetAllNetworkInterfaces();
  472. foreach (var nic in nics)
  473. {
  474. int physical = 0;
  475. if (nic.OperationalStatus == OperationalStatus.Up)
  476. {
  477. if (IsPhysicalNetworkInterface(nic))
  478. {
  479. physical = 1;
  480. }
  481. var name = $"{nic.Description}";
  482. var sname = $"{nic.Name}";
  483. var mac = nic.GetPhysicalAddress().ToString();
  484. var properties = nic.GetIPProperties();
  485. var unicastAddresses = properties.UnicastAddresses;
  486. foreach (var unicast in unicastAddresses)
  487. {
  488. if (unicast.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
  489. {
  490. var ip = unicast.Address.ToString();
  491. Network network = new Network() {id=ShaHashHelper.GetSHA1($"{mac}-{sname}-{name}"), mac=mac, ip=ip, name= name,sname=sname, physical=physical };
  492. if (!string.IsNullOrWhiteSpace(mac.ToString()) && !mac.Equals("000000000000"))
  493. {
  494. device.networks.Add(network);
  495. }
  496. }
  497. }
  498. }
  499. }
  500. //if (_url!.IsNotEmpty())
  501. //{
  502. // List<UriInfo> ports = new List<UriInfo>();
  503. // foreach (var url in _url!)
  504. // {
  505. // Uri uri = new Uri(url);
  506. // device.uris.Add(new UriInfo { port= uri.Port, protocol= uri.Scheme });
  507. // }
  508. //}
  509. //else
  510. //{
  511. // throw new Exception("未获取到端口信息!");
  512. //}
  513. var networks = device.networks.ToList();
  514. if (device.networks.IsNotEmpty())
  515. {
  516. var order= device.networks.OrderByDescending(x => x.physical).ToList();
  517. //for (int i=0; i<order.Count();i++)
  518. //{
  519. // order[i].domain="exam.habook.local";
  520. //}
  521. device.networks=order;
  522. //优先以物理网卡来生成hash,如果没有则以所有网卡生成hash
  523. var physical = order.FindAll(x => x.physical==1);
  524. if (physical.IsNotEmpty())
  525. {
  526. networks=physical;
  527. }
  528. }
  529. StringBuilder sb= new StringBuilder();
  530. sb.Append(device.name);//设备名称
  531. sb.Append(device.os);//系统名称
  532. sb.Append(device.bit);//系统位
  533. sb.Append(device.arch);//指令架构
  534. sb.Append(device.cpu);//cpu核心树
  535. sb.Append(string.Join(",", device.cpuInfos.Select(x=>$"{x.name}{x.hz}")));//cpu名称和频率
  536. sb.Append(device.ram);//内存
  537. sb.Append(string.Join(",", networks.Select(x => $"{x.mac}-{x.name}")));//网卡地址和名称
  538. //暂不使用远程ip和局域网内ip作为hash,可能发生变化
  539. string hashData = ShaHashHelper.GetSHA256(sb.ToString());
  540. device.deviceId=hashData;
  541. device.id= hashData;
  542. return device;
  543. }
  544. public static bool IsPhysicalNetworkInterface(NetworkInterface nic)
  545. {
  546. // 排除虚拟网卡的关键词
  547. string[] virtualKeywords = { "virtual", "hyper-v", "virtualbox", "vmware", "veth","virbr", "tunnel", "docker", "loopback", "vpn","ppp", "slip", "bridge",
  548. "vnic", "vif", "tap", "vlan", "vswitch", "vxlan", "gre", "ipsec", "vrf", "vport", "vnet", "vmac", "vxnet"};
  549. // 检查网卡描述或名称中是否包含虚拟关键词
  550. string description = nic.Description.ToLower();
  551. string name = nic.Name.ToLower();
  552. foreach (var keyword in virtualKeywords)
  553. {
  554. if (description.Contains(keyword,StringComparison.OrdinalIgnoreCase) || name.Contains(keyword, StringComparison.OrdinalIgnoreCase))
  555. {
  556. return false; // 是虚拟网卡
  557. }
  558. }
  559. // 排除一些常见的虚拟网卡类型
  560. if (nic.NetworkInterfaceType == NetworkInterfaceType.Loopback ||
  561. nic.NetworkInterfaceType == NetworkInterfaceType.Tunnel ||
  562. nic.NetworkInterfaceType == NetworkInterfaceType.Ppp ||
  563. nic.NetworkInterfaceType == NetworkInterfaceType.Slip||
  564. nic.NetworkInterfaceType == NetworkInterfaceType.Unknown
  565. )
  566. {
  567. return false; // 是虚拟网卡
  568. }
  569. // 默认认为是物理网卡
  570. return true;
  571. }
  572. /// <summary>
  573. /// 直接获取设备。
  574. /// </summary>
  575. /// <param name="httpContext"></param>
  576. /// <param name="IP"></param>
  577. /// <param name="device_timeSpan"></param>
  578. /// <param name="_azureRedis"></param>
  579. /// <returns></returns>
  580. public static string GetDevice(HttpContext httpContext, IMemoryCache _memoryCache)
  581. {
  582. string IP= GetIP(httpContext);
  583. var cookie = httpContext.Request.Cookies;
  584. string device =string.Empty;
  585. if (cookie != null)
  586. {
  587. ///设备是否存在
  588. foreach (var ck in cookie)
  589. {
  590. if (ck.Key.Equals("device"))
  591. {
  592. //redis如果存在则
  593. var fingerprint = ck.Value.Split("-");
  594. if (fingerprint.Length == 2 && IP.Equals(fingerprint[1]))
  595. {
  596. if (!_memoryCache.TryGetValue<string>($"device:{fingerprint[0]}:{IP}", out device))
  597. {
  598. _memoryCache.Set($"device:{fingerprint[0]}:{IP}", $"{fingerprint[0]}-{IP}");
  599. device = $"{fingerprint[0]}-{IP}";
  600. }
  601. }
  602. }
  603. }
  604. }
  605. return device;
  606. }
  607. public static string GetIP(HttpContext httpContext)
  608. {
  609. var IpPort = httpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
  610. if (string.IsNullOrEmpty(IpPort))
  611. {
  612. IpPort = $"{httpContext.Connection.RemoteIpAddress}";
  613. }
  614. if (IpPort.Contains("::"))
  615. {
  616. IpPort = "127.0.0.1";
  617. }
  618. return IpPort;
  619. }
  620. /// <summary>
  621. /// 初始化设备
  622. /// </summary>
  623. /// <param name="httpContext"></param>
  624. /// <param name="fingerprint">浏览器指纹</param>
  625. /// <param name="IP"></param>
  626. /// <param name="device_timeSpan"></param>
  627. /// <param name="_azureRedis"></param>
  628. /// <returns></returns>
  629. public static string GetDeviceInit(this HttpContext httpContext, string fingerprint, string IP, IMemoryCache _memoryCache)
  630. {
  631. string device = $"{fingerprint}-{IP}";
  632. List<string> cookieString = new List<string>();
  633. var cookie = httpContext.Request.Cookies;
  634. int status = 1;
  635. if (cookie != null)
  636. {
  637. ///设备是否存在
  638. foreach (var ck in cookie)
  639. {
  640. if (ck.Key.Equals("device"))
  641. {
  642. if (device.Contains("-") && device.Contains("."))
  643. {
  644. //如果匹配的是fingerprint-IP 则是已经存在的。
  645. if (ck.Value.Equals(device))
  646. {
  647. //redis如果存在则
  648. _memoryCache.TryGetValue<string>($"device:{fingerprint}:{IP}", out string device_exist);
  649. // 返回的则应该是ck.Value=exist_device_exist的数据
  650. if (device_exist!=null)
  651. {
  652. if (!string.IsNullOrWhiteSpace($"{device_exist}"))
  653. {
  654. //0是代表指纹和IP匹配,正常返回的
  655. status = 1;
  656. }
  657. else
  658. {
  659. //需要新建 fingerprint-IP
  660. status = 1;
  661. }
  662. }
  663. else
  664. {
  665. status = 1;
  666. }
  667. }
  668. else
  669. {
  670. string ck_ip = ck.Value.Split("-")[1];
  671. if (ck_ip.Equals(IP))
  672. {
  673. //传入的指纹和cookie的不一致,仍然以cookie的为准。
  674. status = 1;
  675. fingerprint = ck.Value.Split("-").First();
  676. device = ck.Value;
  677. }
  678. }
  679. }
  680. else
  681. {
  682. //如果匹配的是fingerprint则是一个新的设备。
  683. if (ck.Value.Equals(fingerprint))
  684. {
  685. //检查设备是否被占用
  686. //var device_exist = _azureRedis.GetRedisClient(8).HashExists($"device:{fingerprint}", IP);
  687. _memoryCache.TryGetValue<JsonNode>($"device:{fingerprint}:{IP}", out JsonNode device_exist);
  688. if (device_exist!=null)
  689. {
  690. //需要新建 sha1(fingerprint+uuid)-IP
  691. status = 2;
  692. }
  693. else
  694. {
  695. //0是代表指纹和IP匹配,正常返回的
  696. status = 1;
  697. }
  698. }
  699. else
  700. {
  701. //匹配的都不是,新设备。
  702. status = 1;
  703. }
  704. }
  705. }
  706. else
  707. {
  708. cookieString.Add($"{ck.Key}{ck.Value}");
  709. }
  710. }
  711. }
  712. /*
  713. httpContext.Request.Headers.TryGetValue("accept-language", out var accept_language);
  714. httpContext.Request.Headers.TryGetValue("sec-ch-ua", out var chua);
  715. httpContext.Request.Headers.TryGetValue("sec-ch-ua-platform", out var platform);
  716. httpContext.Request.Headers.TryGetValue("user-agent", out var useragent);
  717. httpContext.Request.Headers.TryGetValue("accept", out var accept);
  718. httpContext.Request.Headers.TryGetValue("accept-encoding", out var accept_encoding);
  719. device = ShaHashHelper.GetSHA1($"{IP}{accept_language}{chua}{platform}{useragent}{accept}{accept_encoding}{string.Join("", cookieString)}");
  720. */
  721. if (status == 2)
  722. {
  723. fingerprint = ShaHashHelper.GetSHA1(fingerprint + Guid.NewGuid().ToString());
  724. device = $"{fingerprint}-{IP}";
  725. }
  726. //else if (status == 1)
  727. //{
  728. // device = $"{fingerprint}-{IP}";
  729. //}
  730. //await _azureRedis.GetRedisClient(8).HashSetAsync($"device:{fingerprint}", IP, new { device }.ToJsonString());
  731. //await _azureRedis.GetRedisClient(8).KeyExpireAsync($"device:{fingerprint}", device_timeSpan);
  732. _memoryCache.Set($"device:{fingerprint}:{IP}", device);
  733. httpContext.Response.Cookies.Append("device", device, new CookieOptions { HttpOnly = true, MaxAge = new TimeSpan(24 * 7, 0, 0) });
  734. return device;
  735. }
  736. }
  737. public class UriInfo
  738. {
  739. public string? protocol { get; set; }
  740. public int port { get; set; }
  741. }
  742. }