using IES.ExamServer.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; using System.Diagnostics; using System.Text.Json.Nodes; using System.Text.Json; using IES.ExamServer.Helper; using System.DrawingCore.Imaging; using System.DrawingCore; using System.IdentityModel.Tokens.Jwt; using IES.ExamServer.Services; using IES.ExamServer.DI; using IES.ExamServer.Helpers; using IES.ExamLib.Models; namespace IES.ExamServer.Controllers { [ApiController] [Route("index")] public class IndexController : BaseController { private readonly IConfiguration _configuration; private readonly IHttpClientFactory _httpClientFactory; private readonly IMemoryCache _memoryCache; private readonly ILogger _logger; private readonly CenterServiceConnectionService _connectionService; private readonly LiteDBFactory _liteDBFactory; public IndexController(ILogger logger, IConfiguration configuration, IHttpClientFactory httpClientFactory, IMemoryCache memoryCache,CenterServiceConnectionService connectionService, LiteDBFactory liteDBFactory) { _logger = logger; _configuration=configuration; _httpClientFactory=httpClientFactory; _memoryCache=memoryCache; _connectionService=connectionService; _liteDBFactory=liteDBFactory; } [HttpPost("generate-certificate")] public async Task GenerateCertificate(JsonNode json) { try { ServerDevice serverDevice = _memoryCache.Get(Constant._KeyServerDevice); if (serverDevice!=null && serverDevice.networks.IsNotEmpty()) { string mac = $"{json["mac"]}"; var network = serverDevice.networks.Find(x => mac.Equals(x.mac)); if (network!=null) { string pathBat = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "template", "certificate.bat"); string pathCer = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "template" ,"certificate.cer"); string text = await System.IO.File.ReadAllTextAsync(pathBat); text = text.Replace("192.168.8.132", network.ip); string pathCertificateBat = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package", "certificate.bat"); await System.IO.File.WriteAllTextAsync(pathCertificateBat, text); string pathCertificateCer = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package", "certificate.cer"); System.IO.File.Copy(pathCer, pathCertificateCer); return Ok(new { code = 200, msg = "生成成功。", bat = "package/certificate.bat", cer = "package/certificate.cer" }); } else { msg="未找到匹配的网卡设备。"; } } else { msg="服务端设备未找到,或网卡设备不存在。"; } } catch (Exception ex) { _logger.LogError($"生成证书和自定义域名映射脚本错误。{ex.Message},{ex.StackTrace}"); msg=$"服务端错误,{ex.Message}"; } return Ok(new { code = 400, msg = msg }); } [HttpPost("list-schools")] public async Task ListSchool(JsonNode json) { string filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package", "schools.json"); string schoolText = await System.IO.File.ReadAllTextAsync(filePath); JsonNode? node = schoolText.ToObject(); return Ok(new {code =200, schools= node?["schools"] }); } [HttpPost("bind-school")] public async Task BindSchool(JsonNode json) { string id=$"{json["id"]}"; string name= $"{json["name"]}"; string fp = $"{json["fp"]}"; if (!string.IsNullOrWhiteSpace(id) && !string.IsNullOrWhiteSpace(name) && !string.IsNullOrWhiteSpace(fp)) { string filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package", "schools.json"); string schoolText = await System.IO.File.ReadAllTextAsync(filePath); List? schools = schoolText.ToObject()?["schools"]?.ToObject>(); School? school = schools?.Find(x => id.Equals(x.id) && name.Equals(x.name)); if (school!=null) { _liteDBFactory.GetLiteDatabase().GetCollection().DeleteAll(); _liteDBFactory.GetLiteDatabase().GetCollection().Upsert(school); IEnumerable schoolsDb = _liteDBFactory.GetLiteDatabase().GetCollection().FindAll(); School? schoolDb = schools?.FirstOrDefault(); _memoryCache.TryGetValue(Constant._KeyServerDevice, out ServerDevice? server); if (server!=null) { server.school = school; } string ip = GetIP(); var device = IndexService.GetDeviceInit(HttpContext, $"{fp}", ip, _memoryCache); int hybrid = 0; _memoryCache.Set(Constant._KeyServerDevice, server); _memoryCache.TryGetValue(Constant._KeyServerCenter, out JsonNode? data); _memoryCache.TryGetValue(Constant._KeyServerDevice, out server); if (data!=null) { hybrid=1; msg="云端服务连接成功!"; return Ok(new { code = 200, msg, data = new { hybrid, device, centerUrl = data["centerUrl"], region = data["region"], ip = data["ip"], nowtime = DateTimeOffset.Now.ToUnixTimeMilliseconds(), server } }); } else { msg="云端服务未连接!"; return Ok(new { code = 200, msg, data = new { hybrid, device, centerUrl = "", region = "局域网·内网", ip = ip, nowtime = DateTimeOffset.Now.ToUnixTimeMilliseconds(), server } }); } } else { return Ok(new { code = 400, msg = "绑定失败!" }); } } else { return Ok(new { code = 400, msg = "参数错误!" }); } } [HttpPost("device")] public IActionResult Device(JsonElement json ) { try { string ip= GetIP(); json.TryGetProperty("fp", out JsonElement fp); var device = IndexService.GetDeviceInit(HttpContext, $"{fp}", ip, _memoryCache); int hybrid = 0, notify = 0 ; _memoryCache.TryGetValue(Constant._KeyServerCenter, out JsonNode? data); _memoryCache.TryGetValue(Constant._KeyServerDevice, out ServerDevice? server); if (_connectionService.notifyIsConnected) { notify=1; } if (_connectionService.centerIsConnected) { hybrid=1; } msg="服务端检测成功!"; return Ok(new { code = 200, msg, data = new { notify, hybrid, device, centerUrl = hybrid==1 ? _connectionService.centerUrl : "", notifyUrl = notify==1 ? _connectionService.notifyUrl : "", region = data?["region"], ip = data!=null ? data?["ip"] : ip, nowtime = DateTimeOffset.Now.ToUnixTimeMilliseconds(), server } }); } catch (Exception ex) { code=500; msg="服务端异常!"; } return Ok(new { code, msg }); } /** { "type":"sms",//qrcode二维码扫码登录:randomCode必传; sms 短信验证登录:randomCode必传,mobile必传 "randomCode", "mobile":"1528377****" } **/ /// /// 登录验证 /// /// /// [HttpPost("login-check")] public async Task LoginCheck(JsonNode json) { try { var type = json["type"]; string? CenterUrl = _configuration.GetValue("ExamServer:CenterUrl"); if (!string.IsNullOrWhiteSpace($"{type}")) { TmdidImplicit? token = null; string x_auth_token = string.Empty; List? schools = null; JsonNode? jsonNode = null; long time = DateTimeOffset.Now.ToUnixTimeMilliseconds(); _memoryCache.TryGetValue(Constant._KeyServerDevice, out ServerDevice? server); School? school = null; if (server!=null) { school = server.school; } switch (true) { //跳过忽略,但是仍然要以访客身份登录 case bool when $"{type}".Equals(ExamConstant.ScopeVisitor): { string id = $"{DateTimeOffset.Now.ToUnixTimeSeconds()}"; string name = $"{school?.name}教师-{Random.Shared.Next(100, 999)}"; x_auth_token = JwtAuthExtension.CreateAuthToken("www.teammodel.cn",id ,name,picture: school?.picture , ExamConstant.JwtSecretKey,ExamConstant.ScopeVisitor,8,schoolID:school?.id,new string[] { "visitor" }, expire: 1); // _memoryCache.Set($"Teacher:{id}", new Teacher { id = id, name = $"{name}", implicit_token = token, picture = null, schools = schools, x_auth_token = x_auth_token }); _liteDBFactory.GetLiteDatabase().GetCollection().Upsert(new Teacher { id = id, name = $"{name}", implicit_token = token, picture = null, schools = schools, x_auth_token = x_auth_token,loginTime=time }); return Ok(new { code = 200,x_auth_token = x_auth_token }); } case bool when $"{type}".Equals("qrcode"): { string randomCode = $"{json["randomCode"]}"; System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12; var rc= _memoryCache.Get($"Login:ExamServer:{school?.id}:{randomCode}"); if (!string.IsNullOrWhiteSpace(rc)) { var response = await _httpClientFactory.CreateClient().GetAsync($"{CenterUrl}/core/qrcode/check?randomcode={randomCode}&school={school?.id}&client=ExamServer"); if (response.IsSuccessStatusCode) { string content = await response.Content.ReadAsStringAsync(); if (!string.IsNullOrWhiteSpace(content)) { jsonNode = content.ToObject(); } else { code=400; msg="随机码验证失败"; } } else { code=400; msg="随机码验证错误"; } } else { code=400; msg="二维码过期"; } break; } case bool when $"{type}".Equals("smspin"): { string pin_code = $"{json["pin_code"]}"; string account = $"{json["account"]}"; var response = await _httpClientFactory.CreateClient().PostAsJsonAsync($"{CenterUrl}/core/sendsms/check", new { pin_code, account,school=school?.id }); if (response.IsSuccessStatusCode) { string content = await response.Content.ReadAsStringAsync(); if (!string.IsNullOrWhiteSpace(content)) { jsonNode = content.ToObject(); } else { code=400; msg="短信验证返回结果为空"; } } else { code=400; msg="短信验证错误"; } break; } } if (jsonNode != null && $"{jsonNode["code"]}".Equals("200")) { token =jsonNode["implicit_token"]?.ToObject(); x_auth_token = $"{jsonNode["x_auth_token"]}"; schools =jsonNode["schools"]?.ToObject>(); var jwt = new JwtSecurityToken(token?.id_token); var id = jwt.Payload.Sub; jwt.Payload.TryGetValue("name", out object? name); jwt.Payload.TryGetValue("picture", out object? picture); //_memoryCache.Set($"Teacher:{id}", new Teacher { id=id, name=$"{name}", implicit_token= token, picture=$"{picture}", schools=schools, x_auth_token=x_auth_token }); _liteDBFactory.GetLiteDatabase().GetCollection().Upsert(new Teacher { id=id, name=$"{name}", implicit_token= token, picture=$"{picture}", schools=schools, x_auth_token=x_auth_token ,loginTime=time }); return Ok(new { code=200,/* implicit_token = token, schools = schools , */ x_auth_token = x_auth_token }); } else { code=400; msg="验证失败"; } } else { code=400; msg="参数错误"; } } catch (Exception ex) { code=500; msg="异常错误"; } return Ok(new { code = code,msg }); } /* */ /// /// 登录模式初始化 /// /// [HttpPost("login-init")] public async Task LoginInit(JsonNode json) { var type = json["type"]; string qrcode = string.Empty; string randomcode = ""; _memoryCache.TryGetValue(Constant._KeyServerDevice, out ServerDevice? server); School? school = null; if (server != null) { school = server.school; } if (school != null) { switch (true) { case bool when $"{type}".Equals("qrcode"): { //.NET Core使用SkiaSharp快速生成二维码 https://cloud.tencent.com/developer/article/2336486 // 生成二维码图片 Random random = new Random(); randomcode = $"{random.Next(1000, 9999)}"; string? CenterUrl = _configuration.GetValue("ExamServer:CenterUrl"); CenterUrl = CenterUrl.Equals("https://localhost:5001") ? "https://www.teammodel.cn" : CenterUrl; string content = $"{CenterUrl}/qrcodelogin?randomcode=Login:ExamServer:{school?.id}:{randomcode}&m=%E6%89%AB%E7%A0%81%E7%99%BB%E5%BD%95&o=1"; var str = QRCodeHelper.GenerateQRCode(content, 300, 300, QRCodeHelper.logo); qrcode = $"data:image/png;base64,{str}"; int ttl = 60; string key = $"Login:ExamServer:{school?.id}:{randomcode}"; _memoryCache.Set(key, randomcode, TimeSpan.FromSeconds(ttl)); var device =IndexService.GetDevice(HttpContext,_memoryCache); _memoryCache.Set($"device:{key}", device); try { if (_connectionService.notifyIsConnected && _connectionService.serverDevice!=null) { string url = $"{_connectionService.notifyUrl!}/third/ies/qrcode-login-register"; //扫描登录,远端设备登记临时随机码 await _httpClientFactory.CreateClient().PostAsJsonAsync(url,new { randomcode= key, deviceid = _connectionService .serverDevice.deviceId}); } } catch (Exception e) { _logger.LogError($"{e.Message},{e.StackTrace}"); } return Ok(new { ttl,code = 200, randomCode = randomcode, qrcode, type }); } case bool when $"{type}".Equals("xqrcode"): { // 生成二维码图片 Random random = new Random(); randomcode = $"{random.Next(1000, 9999)}"; string? CenterUrl = _configuration.GetValue("ExamServer:CenterUrl"); CenterUrl = CenterUrl.Equals("https://localhost:5001") ? "https://www.teammodel.cn" : CenterUrl; string content = $"{CenterUrl}/qrcodelogin?randomcode=Login:ExamServer:{school?.id}:{randomcode}&m=%E6%89%AB%E7%A0%81%E7%99%BB%E5%BD%95&o=1"; Bitmap qrCodeImage = QRCodeHelper.GetBitmap(content, 200, 200); using (MemoryStream stream = new MemoryStream()) { qrCodeImage.Save(stream, ImageFormat.Png); byte[] data = stream.ToArray(); qrcode = $"data:image/png;base64,{Convert.ToBase64String(data)}"; } int ttl = 60; _memoryCache.Set($"Login:ExamServer:{school?.id}:{randomcode}", randomcode, TimeSpan.FromSeconds(ttl)); return Ok(new {ttl, code = 200, randomCode = randomcode, qrcode, type }); } case bool when $"{type}".Equals("smspin"): { int send = 0; if (!string.IsNullOrWhiteSpace($"{json["area"]}") && !string.IsNullOrWhiteSpace($"{json["to"]}")) { string? CenterUrl = _configuration.GetValue("ExamServer:CenterUrl"); string url = $"{CenterUrl}/core/sendsms/pin"; HttpResponseMessage message = await _httpClientFactory.CreateClient().PostAsJsonAsync(url, new { area = json["area"], to = json["to"], lang = "zh-cn" }); if (message.IsSuccessStatusCode) { string content = await message.Content.ReadAsStringAsync(); JsonNode? jsonNode = content?.ToObject(); if (jsonNode != null && int.TryParse($"{jsonNode["send"]}", out int s)) { send = s; } } } return Ok(new { code = 200, send, type }); } } } else if (school == null) { return Ok(new { code = 400,msg="未绑定学校" }); } return Ok(new { code = 400 }); } } }