using Hei.Captcha; using HiTeachCE.Extension; using HiTeachCE.Helpers; using HiTeachCE.Models; using HiTeachCE.Services; using IdentityModel; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; using OpenXmlPowerTools; using Org.BouncyCastle.Ocsp; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Linq.Expressions; using System.Security.Claims; using System.Text.Json; using System.Threading.Tasks; using TEAMModelOS.SDK.Context.Configuration; using TEAMModelOS.SDK.Context.Exception; using TEAMModelOS.SDK.Extension.DataResult.JsonRpcRequest; using TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse; using TEAMModelOS.SDK.Extension.JwtAuth.Models; using TEAMModelOS.SDK.Helper.Common.CollectionHelper; using TEAMModelOS.SDK.Helper.Common.JsonHelper; using TEAMModelOS.SDK.Helper.Security.ShaHash; namespace HiTeachCE.Controllers { [Route("api/[controller]")] [ApiController] public class LoginController : BaseController { public static int smsTTL = 1 * 60; public static int ticketTTL = 1 * 24 * 60 * 60; public static int freeTTL = 7 * 24 * 60 * 60; public static int deviceTTL = 1 * 24 * 60 * 60; public static string freeOrg = "7f847a9f05224184a5d01ee69a6b00d6"; public static string model_teach = "teach"; public static string model_prepare = "prepare"; private readonly LecturerService lecturerService; private readonly OrganizationService organizationService; private readonly MemberService memberService; private readonly ActivationCodeService activationCodeService; private readonly SecurityCodeHelper securityCode; public LoginController(LecturerService lecturer, OrganizationService organization, MemberService member, ActivationCodeService activationCode, SecurityCodeHelper _securityCode) { lecturerService = lecturer; organizationService = organization; memberService = member; activationCodeService = activationCode; securityCode = _securityCode; } /// /// 注册装置 /// /// /// [HttpPost("regist")] [Authorize(Policy = "lecturer")] public BaseJosnRPCResponse Regist(JosnRPCRequest> request) { JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom(); string unionid = GetLoginUser(JwtClaimTypes.Id); /** "params": { "deviceId": "f67fb5dd-ee1b-d3b7-9b95-61022d7e8acd", "clientId": "931dee8c-74be-4c9b-a602-c74583b0e985", } */ if (request.@params.TryGetValue("deviceId", out string deviceId) && request.@params.TryGetValue("orgCode", out string orgCode) && string.IsNullOrEmpty(unionid)) { Dictionary dict = ActivationValid(orgCode, unionid); if (dict.TryGetValue("flag", out object flag) && bool.Parse(flag.ToString())) { if (RedisHelper.HExists("device:" + deviceId, orgCode)) { } else { RedisHelper.HSet("device:" + deviceId, orgCode, unionid); RedisHelper.Expire("device:" + deviceId, deviceTTL); } return builder.Data(new Dictionary { { "deviceId", deviceId } }).build(); } else { throw new BizException("授权失败!", 2); } } else { throw new BizException("参数错误!", 2); } } /// /// 创建教室 /// /// /// [HttpPost("createGroup")] [Authorize] public BaseJosnRPCResponse CreateGroup(JosnRPCRequest> request) { /** "params": { "deviceId": "f67fb5dd-ee1b-d3b7-9b95-61022d7e8acd", "doBoundGroupNum": false, "extraInfo": {} } */ JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom(); string ClientId =// new List() { "fb564dde14df423cafac2085936e3b96" }; GetLoginUser(JwtClaimTypes.ClientId); string groupNum; if (request.@params.TryGetValue("deviceId", out string deviceId) && string.IsNullOrEmpty(ClientId)) { if (RedisHelper.HExists("device:" + ClientId, deviceId)) { groupNum = RedisHelper.HGet("device:" + ClientId, deviceId); if (string.IsNullOrEmpty(groupNum)) { do { groupNum = RandGroupNum(); } while (RedisHelper.Exists("group:" + groupNum)); RedisHelper.HSet("group:" + groupNum, deviceId, null); RedisHelper.Expire("group:" + groupNum, deviceTTL); RedisHelper.HSet("device:" + ClientId, deviceId, groupNum); } } else { throw new BizException("装置未注册", 2); } } else { throw new BizException("参数错误", 2); } return builder.Data(groupNum).build(); } public string RandGroupNum() { Random random = new Random(); String result = ""; for (int i = 0; i < 6; i++) { result += random.Next(0, 10); } return result; } /// /// 加入教室 /// /// /// [HttpPost("joinGroup")] [Authorize] public BaseJosnRPCResponse JoinGroup(JosnRPCRequest> request) { string ClientId = GetLoginUser(JwtClaimTypes.ClientId); string Unionid = GetLoginUser(JwtClaimTypes.Id); string Role = GetLoginUser(JwtClaimTypes.Role); JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom(); Dictionary dict; /** "params": { "deviceId": "f67fb5dd-ee1b-d3b7-9b95-61022d7e8acd", "groupNum": "818288" } */ if (request.@params.TryGetValue("deviceId", out string deviceId) && request.@params.TryGetValue("groupNum", out string groupNum) && !string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(groupNum) ) { if (RedisHelper.Exists("group:" + groupNum)) { dict = MqttInfo(ClientId, deviceId, groupNum, Unionid, Role); } else { throw new BizException("教室不存在", 2); } } else { throw new BizException("参数错误", 2); } return builder.Data(dict).build(); } private static Dictionary MqttInfo( string ClientId, string deviceId, string groupNum, string Unionid, string Role) { string brokerHostName = BaseConfigModel.Configuration["brokerHostName"]; Dictionary dict = new Dictionary(); string password = brokerHostName + "/" + groupNum + "/" + deviceId + "/" + ClientId; //发给前端使用的 string h1 = BCrypt.Net.BCrypt.HashPassword(password); //后端存储使用的 string h2 = BCrypt.Net.BCrypt.HashPassword(h1, BCrypt.Net.SaltRevision.Revision2); bool validPassword = BCrypt.Net.BCrypt.Verify(h1, h2); string uname = password; Dictionary connectInfo = new Dictionary { { "brokerHostName", brokerHostName }, { "brokerHostNameWSS", "wss://" +brokerHostName+"/mqtt"} , { "clientID", deviceId }, //使用BCrypt加密 { "password",h1} , { "username",uname} }; Dictionary subscribeTopic = BaseConfigModel.Configuration.GetSection("SubscribeTopic").Get>(); subscribeTopic["receiveMsg"] = subscribeTopic["receiveMsg"].Replace("{deviceId}", deviceId); Dictionary publishTopic = BaseConfigModel.Configuration.GetSection("PublishTopic").Get>(); publishTopic["sendMsg"] = publishTopic["sendMsg"].Replace("{deviceId}", deviceId).Replace("{groupNum}", groupNum); dict.Add("mqtt", new Dictionary() { { "connectInfo", connectInfo }, { "publishTopic", publishTopic }, { "subscribeTopic", subscribeTopic } }); List topic = new List(); topic.AddRange(publishTopic.Values.ToList()); topic.AddRange(subscribeTopic.Values.ToList()); MQTTInfo mqtt = new MQTTInfo { brokerHostName = brokerHostName, brokerHostNameWSS = "wss://" + brokerHostName + "/mqtt", clientID = deviceId, //使用BCrypt加密 password = h2, username = uname, topic = topic }; var groupMember = new MQTTMember { clientId = ClientId, deviceId = deviceId, unionid = Unionid , role = "lecturer", groupNum = groupNum }; RedisHelper.HSet("group:" + groupNum, deviceId, groupMember); RedisHelper.HSet("mqtt:" + deviceId, deviceId, mqtt); RedisHelper.Expire("mqtt:" + deviceId, deviceTTL); return dict; } /// /// 认证 /// /// /// [HttpPost("adminAuth")] public BaseJosnRPCResponse AdminAuth(JosnRPCRequest> request) { JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom(); if (request.@params.TryGetValue("ticket", out string ticket)){ if (RedisHelper.Exists("ticket:" + ticket)) { string[] vals = RedisHelper.HVals("ticket:" + ticket); if (vals != null && vals.Length > 0) { string cellphone = vals[0]; Expression> linq = null; linq = m => m.cellphone == cellphone; List lecturers = lecturerService.GetList(linq); if (lecturers.IsNotEmpty()) { List RootUsers= BaseConfigModel.Configuration.GetSection("RootUser").Get>(); ClaimModel claimModel = new ClaimModel { Scope = "Admin" }; claimModel.Claims.Add(new Claim(JwtClaimTypes.Name, lecturers[0].username)); claimModel.Claims.Add(new Claim(JwtClaimTypes.Id, lecturers[0].unionid)); claimModel.Claims.Add(new Claim(JwtClaimTypes.PhoneNumber, lecturers[0].cellphone)); if (RootUsers.Contains(lecturers[0].cellphone)) { // claimModel.Claims.Add(new Claim(JwtClaimTypes.Role, role)); // 可以将一个用户的多个角色全部赋予; claimModel.Claims.AddRange("root".Split(',').Select(s => new Claim(JwtClaimTypes.Role, s))); } else { // claimModel.Claims.Add(new Claim(JwtClaimTypes.Role, role)); // 可以将一个用户的多个角色全部赋予; claimModel.Claims.AddRange("admin".Split(',').Select(s => new Claim(JwtClaimTypes.Role, s))); } // claimModel.Claims.Add(new Claim(JwtClaimTypes.Role, role)); //claimModel.Claims.Add(new Claim(JwtClaimTypes.ClientId, activationCodes[0].clientId)); // claimModel.Claims.Add(new Claim("org", orgCode)); JwtResponse jwtResponse = JwtHelper.IssueJWT(claimModel); //string sha = ShaHashHelper.GetSHA1(jwtResponse.Access_token); //RedisHelper.HSet("jwt:"+sha,sha,jwtResponse.Access_token ); //RedisHelper.Expire("jwt:" + sha, 86400); // return jwtResponse; } } } } return null; } /// /// 教学认证 /// /// /// [HttpPost("auth")] [Authorize(Policy = "lecturer")] public BaseJosnRPCResponse Auth(JosnRPCRequest request) { JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom(); string unionid = GetLoginUser(JwtClaimTypes.Id); Expression> mlinq = null; mlinq = m => m.unionid == unionid; List> dict = new List>(); List members = memberService.GetList(mlinq); if (members.IsNotEmpty()) { foreach (var code in members) { dict.Add(ActivationValid(code.orgCode, unionid)); } } else { long time = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); ///处理该机构是否激活人数达到上线 Expression> limitlinq = null; limitlinq = m => m.orgCode == freeOrg && (m.expires > time || m.expires == -1) && m.status == 1; List countMembers = memberService.GetList(limitlinq); Expression> alinq = null; alinq = m => m.orgCode == freeOrg && m.status == 1; List activationCodes = activationCodeService.GetList(alinq); if (activationCodes.IsNotEmpty()) { //判断组织机构人员是否已经达到最大激活数量 if (countMembers.IsNotEmpty() && countMembers.Count >= activationCodes[0].maximum) { //throw new BizException(":HiTeachCE(测试)授权人数超过上限!", 2); } else { Member member = new Member { id = Guid.NewGuid().ToString(), orgCode = freeOrg, role = "lecturer", status = 1, expires = time + freeTTL, unionid = unionid }; bool flag = memberService.Insert(member); if (flag) { dict.Add(ActivationValid(freeOrg, unionid)); } else { //throw new BizException("无法加入:HiTeachCE(测试)!", 2); } } } else { } } return builder.Data(dict).build(); } public Dictionary ActivationValid(string orgCode, string unionid) { //调用ActivationCode Expression> olinq = null; olinq = m => m.code == orgCode && m.status == 1; Organization org = organizationService.GetList(olinq).First(); Dictionary dict = new Dictionary() { { "org", new { orgCode = "", name = org.name } }, { "flag", false } }; //验证组织机构的激活码状态,时间,最大人数 Expression> linq = null; linq = m => m.orgCode == org.code; List activationCodes = activationCodeService.GetList(linq); long time = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(); if (activationCodes[0].expires > time || activationCodes[0].expires == -1) { int max = activationCodes[0].maximum; Expression> mlinq = null; mlinq = l => l.orgCode == org.code; List members = memberService.GetList(mlinq); if (members.Count >= max) { dict.Add("msg", "产品授权人数超过上限!"); } else { if (members.Where(x => x.status == 1 && (x.expires > time || x.expires == -1)).Select(x => x.unionid).ToList().Contains(unionid)) { dict["org"] = new { orgCode = org.code, name = org.name }; dict.Add("flag", true); } else { dict.Add("msg", "组织机构未对该用户授权或已经过期!"); } } } else { dict.Add("msg", "产品授权已经过期!"); } return dict; } /// /// 登录 /// /// /// [HttpPost("phoneLogin")] public async Task PhoneLogin(JosnRPCRequest> request) { JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom(); if (request.@params.TryGetValue("cellphone", out string cellphone) && request.@params.TryGetValue("smsCode", out string smsCode) ) { string ticket = ShaHashHelper.GetSHA1(cellphone + smsCode); if (RedisHelper.Exists("ticket:" + ticket)) { Dictionary dict = UserValid(cellphone); dict.Add("ticket", ticket); return builder.Data(dict).build(); } if (RedisHelper.Exists(cellphone)) { List RootUsers = BaseConfigModel.Configuration.GetSection("RootUser").Get>(); if (RootUsers.Contains(cellphone) && smsCode.Equals("000000")) { ///验证通过 验证信息存放在reids RedisHelper.HSet("ticket:" + ticket, cellphone, cellphone); RedisHelper.Expire("ticket:" + ticket, ticketTTL); Dictionary dict = UserValid(cellphone); dict.Add("ticket", ticket); return builder.Data(dict).build(); } else { string[] vals = RedisHelper.HVals(cellphone); if (vals != null && vals.Length > 0) { string resdata = await HttpClientHelper.Post( BaseConfigModel.Configuration["JPush:Valid"].Replace("{msg_id}", vals[0]), BaseConfigModel.Configuration["JPush:AppKey"], BaseConfigModel.Configuration["JPush:Secret"], new Dictionary { { "code", smsCode } }); JsonElement element = resdata.FromApiJson(); if (element.TryGetProperty("is_valid", out JsonElement json)) { if (json.GetBoolean()) { ///验证通过 验证信息存放在reids RedisHelper.HSet("ticket:" + ticket, cellphone, cellphone); RedisHelper.Expire("ticket:" + ticket, ticketTTL); Dictionary dict = UserValid(cellphone); dict.Add("ticket", ticket); return builder.Data(dict).build(); } else { throw new BizException("短信验证码过期!", 2); } } else { throw new BizException("短信验证码过期!", 2); } } else { throw new BizException("短信验证码过期!", 2); } } } else { throw new BizException("短信验证码过期!", 2); } } else { throw new BizException("手机号、短信验证码未填写!", 2); } //如果验证通过则将验证信息缓存至redis 以防再次远程验证不通过 //string uid = ""; //List organizations = GetOrgByUid(uid); //return builder.Data(organizations).build(); } // [HttpPost("GetOrgByUid")] public List GetOrgByUid(string uid) { Expression> mlinq = null; mlinq = m => m.unionid == uid && (m.expires > 0 || m.expires == -1) && m.status == 1; List members = memberService.GetList(mlinq); if (members.IsNotEmpty()) { Expression> olinq = null; olinq = o => members.Select(x => x.orgCode).ToList().Contains(o.code) && o.status == 1; List organizations = organizationService.GetList(olinq); ///返回前端后倒计时10秒自动选择组织机构,以防再次验证的时候 reids过期 return organizations; } else { return null; } } private Dictionary UserValid(string cellphone) { Expression> linq = null; linq = m => m.cellphone == cellphone; List lecturers = lecturerService.GetList(linq); if (lecturers.IsNotEmpty()) { var lecturer = lecturers[0]; ClaimModel claimModel = new ClaimModel { Scope = "WebApp" }; claimModel.Claims.Add(new Claim(JwtClaimTypes.Name, lecturer.username)); claimModel.Claims.Add(new Claim(JwtClaimTypes.Id, lecturer.unionid)); claimModel.Claims.Add(new Claim(JwtClaimTypes.PhoneNumber, lecturer.cellphone)); List RootUsers = BaseConfigModel.Configuration.GetSection("RootUser").Get>(); string role = "admin,lecturer"; if (RootUsers.Contains(lecturers[0].cellphone)) { role = "root," + role; } // claimModel.Claims.Add(new Claim(JwtClaimTypes.Role, role)); // 可以将一个用户的多个角色全部赋予; claimModel.Claims.AddRange(role.Split(',').Select(s => new Claim(JwtClaimTypes.Role, s))); // claimModel.Claims.Add(new Claim(JwtClaimTypes.ClientId, activationCodes[0].clientId)); // claimModel.Claims.Add(new Claim("org", orgCode)); JwtResponse jwtResponse = JwtHelper.IssueJWT(claimModel); return new Dictionary { { "status", 2 }, { "jwt", jwtResponse } }; } else { //不存在用户则新增一个 Random random = new Random(); string seed = new string(Constant.az09); string pfx = ""; for (int i = 0; i < 4; i++) { string c = seed.ToCharArray()[random.Next(0, seed.Length)] + ""; seed.Replace(c, ""); pfx = pfx + c; } return new Dictionary { { "status",1}, { "user",new Lecturer { id= Guid.NewGuid().ToString(), unionid= Guid.NewGuid().ToString("N"), username=cellphone+"手机用户", password="", account="hitmd-"+cellphone.Substring(cellphone.Length-4,4)+"#"+pfx, areaCode="86", registerTime=new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(), status=1, setaccount=0, cellphone=cellphone } } }; } } /// /// 初始化登录 /// /// /// [HttpPost("init")] public BaseJosnRPCResponse Init(JosnRPCRequest request) { JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom(); if (!string.IsNullOrEmpty(request.@params)) { var code = securityCode.GetRandomEnDigitalText(4).ToLower(); var imgbyte = securityCode.GetGifEnDigitalCodeByte(code); string base64 = "data:image/png;base64," + Convert.ToBase64String(imgbyte); RedisHelper.HSet("captcha:" + request.@params, request.@params, code); RedisHelper.Expire("captcha:" + request.@params, smsTTL); return builder.Data(base64).build(); } else { throw new BizException("随机码为空!", 2); } } /// /// 发送短信 /// /// /// [HttpPost("sendSMS")] public async Task SendSMS(JosnRPCRequest request) { JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom(); string captcha = RedisHelper.HGet("captcha:" + request.@params.randCode, request.@params.randCode); List RootUsers = BaseConfigModel.Configuration.GetSection("RootUser").Get>(); bool f = !string.IsNullOrEmpty(captcha) && captcha.Equals(request.@params.captcha.ToLower()); bool s= RootUsers.Contains(request.@params.cellphone) && request.@params.captcha.ToLower().Equals("0000"); if (f||s) { string key = request.@params.cellphone; if (RedisHelper.Exists(key)) { string[] vals = RedisHelper.HVals(key); if (vals != null && vals.Length > 0) { Dictionary data = new Dictionary() { { "msgid", vals[0] }, { "repeat", true } }; return builder.Data(data).build(); } else { return builder.Data(await SendMsg(key)).build(); } } else { return builder.Data(await SendMsg(key)).build(); } } else { throw new BizException("验证码错误!", 2); } } private static async Task> SendMsg(string key) { List RootUsers = BaseConfigModel.Configuration.GetSection("RootUser").Get>(); Dictionary data = new Dictionary() { { "mobile", key }, { "temp_id", 1 }, { "sign_id", "" } }; if (RootUsers.Contains(key)) { string msgidstr = key; RedisHelper.Del(new string[] { key }); RedisHelper.HSet(key, key, msgidstr); RedisHelper.Expire(key, smsTTL); return new Dictionary() { { "msgid", msgidstr }, { "repeat", false } }; } else { string resdata = await HttpClientHelper.Post( BaseConfigModel.Configuration["JPush:Push"], BaseConfigModel.Configuration["JPush:AppKey"], BaseConfigModel.Configuration["JPush:Secret"], data); JsonElement element = resdata.FromApiJson(); if (element.TryGetProperty("msg_id", out JsonElement msgid)) { string msgidstr = msgid.GetString(); RedisHelper.Del(new string[] { key }); RedisHelper.HSet(key, key, msgidstr); RedisHelper.Expire(key, smsTTL); return new Dictionary() { { "msgid", msgidstr }, { "repeat", false } }; } else { throw new BizException("短信发送失败!", 2); } } } } }