LoginController.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. using DingTalk.Api;
  2. using DingTalk.Api.Request;
  3. using DingTalk.Api.Response;
  4. using Hei.Captcha;
  5. using HiTeachCE.Dtos;
  6. using HiTeachCE.Extension;
  7. using HiTeachCE.Helpers;
  8. using HiTeachCE.Models;
  9. using HiTeachCE.Services;
  10. using IdentityModel;
  11. using Microsoft.AspNetCore.Authorization;
  12. using Microsoft.AspNetCore.Mvc;
  13. using Microsoft.Extensions.Configuration;
  14. using Microsoft.Extensions.Options;
  15. using OpenXmlPowerTools;
  16. using Org.BouncyCastle.Ocsp;
  17. using SshNet.Security.Cryptography;
  18. using System;
  19. using System.Collections.Generic;
  20. using System.ComponentModel.DataAnnotations;
  21. using System.Linq;
  22. using System.Linq.Expressions;
  23. using System.Security.Claims;
  24. using System.Text.Json;
  25. using System.Threading.Tasks;
  26. using System.Web;
  27. using TEAMModelOS.SDK.Context.Configuration;
  28. using TEAMModelOS.SDK.Context.Exception;
  29. using TEAMModelOS.SDK.Extension.DataResult.JsonRpcRequest;
  30. using TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse;
  31. using TEAMModelOS.SDK.Extension.JwtAuth.Models;
  32. using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
  33. using TEAMModelOS.SDK.Helper.Common.JsonHelper;
  34. using TEAMModelOS.SDK.Helper.Security.ShaHash;
  35. namespace HiTeachCE.Controllers
  36. {
  37. [Route("api/[controller]")]
  38. [ApiController]
  39. public class LoginController : BaseController
  40. {
  41. public static int smsTTL = 4 * 60;
  42. public static int ticketTTL = 1 * 24 * 60 * 60;
  43. //public static int freeTTL = 7 * 24 * 60 * 60;
  44. public static int deviceTTL = 1 * 24 * 60 * 60;
  45. public static string freeOrg = "7f847a9f05224184a5d01ee69a6b00d6";
  46. public static string model_teach = "teach";
  47. public static string model_prepare = "prepare";
  48. private readonly LecturerService lecturerService;
  49. private readonly OrganizationService organizationService;
  50. private readonly MemberService memberService;
  51. private readonly ActivationCodeService activationCodeService;
  52. private readonly SecurityCodeHelper securityCode;
  53. public LoginController(LecturerService lecturer, OrganizationService organization, MemberService member, ActivationCodeService activationCode, SecurityCodeHelper _securityCode)
  54. {
  55. lecturerService = lecturer;
  56. organizationService = organization;
  57. memberService = member;
  58. activationCodeService = activationCode;
  59. securityCode = _securityCode;
  60. }
  61. /// <summary>
  62. /// 登录
  63. /// </summary>
  64. /// <param name="request"></param>
  65. /// <returns></returns>
  66. [HttpPost("dingLogin")]
  67. public BaseJosnRPCResponse DingLogin(JosnRPCRequest<string> request)
  68. {
  69. JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom();
  70. string accessKey = BaseConfigModel.Configuration["DingAuth:appId"];
  71. string appSecret = BaseConfigModel.Configuration["DingAuth:appSecret"];
  72. IDingTalkClient client = new DefaultDingTalkClient(BaseConfigModel.Configuration["DingAuth:getuserinfo_bycode"]);
  73. OapiSnsGetuserinfoBycodeRequest req = new OapiSnsGetuserinfoBycodeRequest();
  74. req.TmpAuthCode = request.@params;
  75. OapiSnsGetuserinfoBycodeResponse rsp = client.Execute(req, accessKey, appSecret);
  76. if (rsp.UserInfo != null && !string.IsNullOrEmpty(rsp.UserInfo.Unionid))
  77. {
  78. ///验证通过 验证信息存放在reids
  79. RedisHelper.HSet("TmpAuthCode:" + request.@params, request.@params, new DingUserInfo
  80. {
  81. Unionid = rsp.UserInfo.Unionid,
  82. Nick = rsp.UserInfo.Nick,
  83. Openid = rsp.UserInfo.Openid
  84. });
  85. RedisHelper.Expire("TmpAuthCode:" + request.@params, ticketTTL);
  86. Expression<Func<Lecturer, bool>> linq = null;
  87. linq = l => l.dingUnionid == rsp.UserInfo.Unionid;
  88. List<Lecturer> list = lecturerService.GetList(linq);
  89. if (list.IsNotEmpty() && !string.IsNullOrEmpty(list[0].cellphone))
  90. {
  91. RedisHelper.HSet("ticket:" + request.@params, list[0].cellphone, list[0].cellphone);
  92. RedisHelper.Expire("ticket:" + request.@params, ticketTTL);
  93. Dictionary<string, object> dict = UserValid(list[0].cellphone);
  94. list[0].dingOpenid = rsp.UserInfo.Openid;
  95. list[0].dingUnionid = rsp.UserInfo.Unionid;
  96. list[0].dingNick = rsp.UserInfo.Nick;
  97. lecturerService.Update(list[0]);
  98. dict.Add("ticket", request.@params );
  99. return builder.Data(dict).build();
  100. }
  101. else
  102. {
  103. Dictionary<string, object> dict = new Dictionary<string, object> {
  104. { "status",1},
  105. };
  106. dict.Add("TmpAuthCode", request.@params);
  107. return builder.Data(dict).build();
  108. }
  109. }
  110. else
  111. {
  112. throw new BizException("钉钉后端验证失败", 2);
  113. }
  114. }
  115. /// <summary>
  116. /// 重置密码
  117. /// </summary>
  118. /// <param name="request"></param>
  119. /// <returns></returns>
  120. [HttpPost("resetPassword")]
  121. public BaseJosnRPCResponse ResetPassword(JosnRPCRequest<UserPwdRandom> request)
  122. {
  123. JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom();
  124. string captcha = RedisHelper.HGet<string>("captcha:" + request.@params.randCode, request.@params.randCode);
  125. if (!string.IsNullOrEmpty(captcha) && captcha.Equals(request.@params.captcha.ToLower()))
  126. {
  127. string account = request.@params.account;
  128. Expression<Func<Lecturer, bool>> expression = null;
  129. expression = e => e.cellphone == account;
  130. List<Lecturer> lecturers = lecturerService.GetList(expression);
  131. if (lecturers.IsNotEmpty())
  132. {
  133. Lecturer lecturer = lecturers[0];
  134. string validPassword = BCrypt.Net.BCrypt.HashPassword(request.@params.password);
  135. lecturer.password = validPassword;
  136. return builder.Data(lecturerService.Update(lecturer)).build();
  137. }
  138. else
  139. {
  140. throw new BizException("手机号未注册!", 2);
  141. }
  142. }
  143. else
  144. {
  145. throw new BizException("验证码错误!", 2);
  146. }
  147. }
  148. /// <summary>
  149. /// 登录
  150. /// </summary>
  151. /// <param name="request"></param>
  152. /// <returns></returns>
  153. [HttpPost("accountLogin")]
  154. public BaseJosnRPCResponse AccountLogin(JosnRPCRequest<UserPwdRandom> request)
  155. {
  156. JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom();
  157. string captcha = RedisHelper.HGet<string>("captcha:" + request.@params.randCode, request.@params.randCode);
  158. if (!string.IsNullOrEmpty(captcha) && captcha.Equals(request.@params.captcha.ToLower()))
  159. {
  160. string account = request.@params.account;
  161. Expression<Func<Lecturer, bool>> expression = null;
  162. expression = e => e.account == account;
  163. expression = expression.Or(x => x.cellphone == account);
  164. List<Lecturer> lecturers = lecturerService.GetList(expression);
  165. if (lecturers.IsNotEmpty())
  166. {
  167. Lecturer lecturer = lecturers[0];
  168. if (!string.IsNullOrEmpty(lecturer.password))
  169. {
  170. bool validPassword = BCrypt.Net.BCrypt.Verify(request.@params.password, lecturer.password);
  171. if (validPassword)
  172. {
  173. Dictionary<string, object> dict = UserValid(lecturer.cellphone);
  174. return builder.Data(dict).build();
  175. }
  176. else {
  177. throw new BizException("账号或密码错误!", 2);
  178. }
  179. }
  180. else {
  181. throw new BizException("密码未设置,请使用其他方式登录并设置密码!", 2);
  182. }
  183. }
  184. else
  185. {
  186. throw new BizException("账号或密码错误!", 2);
  187. }
  188. }
  189. else {
  190. throw new BizException("验证码错误!",2);
  191. }
  192. }
  193. /// <summary>
  194. /// 登录
  195. /// </summary>
  196. /// <param name="request"></param>
  197. /// <returns></returns>
  198. [HttpPost("phoneLogin")]
  199. public async Task<BaseJosnRPCResponse> PhoneLogin(JosnRPCRequest<Dictionary<string, string>> request)
  200. {
  201. JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom();
  202. if (request.@params.TryGetValue("cellphone", out string cellphone) &&
  203. request.@params.TryGetValue("smsCode", out string smsCode)
  204. )
  205. {
  206. string ticket = ShaHashHelper.GetSHA1(cellphone + smsCode);
  207. if (RedisHelper.Exists("ticket:" + ticket))
  208. {
  209. Dictionary<string, object> dict = UserValid(cellphone);
  210. dict.Add("ticket", ticket);
  211. return builder.Data(dict).build();
  212. }
  213. if (RedisHelper.Exists(cellphone))
  214. {
  215. List<string> RootUsers = BaseConfigModel.Configuration.GetSection("RootUser").Get<List<string>>();
  216. if (RootUsers.Contains(cellphone) && smsCode.Equals("000000"))
  217. {
  218. ///验证通过 验证信息存放在reids
  219. RedisHelper.HSet("ticket:" + ticket, cellphone, cellphone);
  220. RedisHelper.Expire("ticket:" + ticket, ticketTTL);
  221. Dictionary<string, object> dict = UserValid(cellphone);
  222. dict.Add("ticket", ticket);
  223. return builder.Data(dict).build();
  224. }
  225. else
  226. {
  227. string[] vals = RedisHelper.HVals<string>(cellphone);
  228. if (vals != null && vals.Length > 0)
  229. {
  230. string resdata = await HttpClientHelper.Post(
  231. BaseConfigModel.Configuration["JPush:Valid"].Replace("{msg_id}", vals[0]),
  232. BaseConfigModel.Configuration["JPush:AppKey"],
  233. BaseConfigModel.Configuration["JPush:Secret"], new Dictionary<string, object> { { "code", smsCode } });
  234. JsonElement element = resdata.FromApiJson<JsonElement>();
  235. if (element.TryGetProperty("is_valid", out JsonElement json))
  236. {
  237. if (json.GetBoolean())
  238. {
  239. ///验证通过 验证信息存放在reids
  240. RedisHelper.HSet("ticket:" + ticket, cellphone, cellphone);
  241. RedisHelper.Expire("ticket:" + ticket, ticketTTL);
  242. Dictionary<string, object> dict = UserValid(cellphone);
  243. dict.Add("ticket", ticket);
  244. return builder.Data(dict).build();
  245. }
  246. else
  247. {
  248. throw new BizException("短信验证码过期!", 2);
  249. }
  250. }
  251. else
  252. {
  253. throw new BizException("短信验证码过期!", 2);
  254. }
  255. }
  256. else
  257. {
  258. throw new BizException("短信验证码过期!", 2);
  259. }
  260. }
  261. }
  262. else
  263. {
  264. throw new BizException("短信验证码过期!", 2);
  265. }
  266. }
  267. else
  268. {
  269. throw new BizException("手机号、短信验证码未填写!", 2);
  270. }
  271. //如果验证通过则将验证信息缓存至redis 以防再次远程验证不通过
  272. //string uid = "";
  273. //List<Organization> organizations = GetOrgByUid(uid);
  274. //return builder.Data(organizations).build();
  275. }
  276. // [HttpPost("GetOrgByUid")]
  277. public List<Organization> GetOrgByUid(string uid)
  278. {
  279. Expression<Func<Member, bool>> mlinq = null;
  280. mlinq = m => m.unionid == uid && m.status == 1;
  281. List<Member> members = memberService.GetList(mlinq);
  282. if (members.IsNotEmpty())
  283. {
  284. Expression<Func<Organization, bool>> olinq = null;
  285. olinq = o => members.Select(x => x.orgCode).ToList().Contains(o.code) && o.status == 1;
  286. List<Organization> organizations = organizationService.GetList(olinq);
  287. ///返回前端后倒计时10秒自动选择组织机构,以防再次验证的时候 reids过期
  288. return organizations;
  289. }
  290. else { return null; }
  291. }
  292. private Dictionary<string, object> UserValid(string cellphone)
  293. {
  294. Expression<Func<Lecturer, bool>> linq = null;
  295. linq = m => m.cellphone == cellphone;
  296. List<Lecturer> lecturers = lecturerService.GetList(linq);
  297. if (lecturers.IsNotEmpty())
  298. {
  299. var lecturer = lecturers[0];
  300. ClaimModel claimModel = new ClaimModel
  301. {
  302. Scope = "WebApp"
  303. };
  304. claimModel.Claims.Add(new Claim(JwtClaimTypes.Name, lecturer.username));
  305. claimModel.Claims.Add(new Claim(JwtClaimTypes.Id, lecturer.unionid));
  306. claimModel.Claims.Add(new Claim(JwtClaimTypes.PhoneNumber, lecturer.cellphone));
  307. List<string> RootUsers = BaseConfigModel.Configuration.GetSection("RootUser").Get<List<string>>();
  308. string role = "admin,lecturer";
  309. if (RootUsers.Contains(lecturers[0].cellphone))
  310. {
  311. role = "root," + role;
  312. }
  313. // claimModel.Claims.Add(new Claim(JwtClaimTypes.Role, role));
  314. // 可以将一个用户的多个角色全部赋予;
  315. claimModel.Claims.AddRange(role.Split(',').Select(s => new Claim(JwtClaimTypes.Role, s)));
  316. // claimModel.Claims.Add(new Claim(JwtClaimTypes.ClientId, activationCodes[0].clientId));
  317. // claimModel.Claims.Add(new Claim("org", orgCode));
  318. JwtResponse jwtResponse = JwtHelper.IssueJWT(claimModel);
  319. lecturer.password = "";
  320. Expression<Func<Member, bool>> expression = null;
  321. expression = m => m.unionid == lecturer.unionid;
  322. bool access = memberService.GetList(expression).IsNotEmpty();
  323. return new Dictionary<string, object> { { "status", 2 }, { "jwt", jwtResponse }, { "user", lecturer },{ "access", access } };
  324. }
  325. else
  326. {
  327. //不存在用户则新增一个
  328. Random random = new Random();
  329. string seed = new string(Constant.az09);
  330. string pfx = "";
  331. for (int i = 0; i < 4; i++)
  332. {
  333. string c = seed.ToCharArray()[random.Next(0, seed.Length)] + "";
  334. seed.Replace(c, "");
  335. pfx = pfx + c;
  336. }
  337. return new Dictionary<string, object> {
  338. { "status",1},
  339. { "user",new Lecturer
  340. {
  341. id= Guid.NewGuid().ToString(),
  342. unionid= Guid.NewGuid().ToString("N"),
  343. username=cellphone+"手机用户",
  344. password="",
  345. account="hitmd-"+cellphone.Substring(cellphone.Length-4,4)+"#"+pfx,
  346. areaCode="86",
  347. registerTime=new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds(),
  348. status=1,
  349. setaccount=0,
  350. cellphone=cellphone,
  351. avatar= "https://cdhabook.teammodel.cn/avatar/usertile"+random.Next(10, 44)+".png"
  352. }
  353. }
  354. };
  355. }
  356. }
  357. /// <summary>
  358. /// 钉钉初始化登录
  359. /// </summary>
  360. /// <param name="request"></param>
  361. /// <returns></returns>
  362. [HttpPost("dingConfig")]
  363. public BaseJosnRPCResponse DingConfig(JosnRPCRequest<object> request)
  364. {
  365. JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom();
  366. string appId = BaseConfigModel.Configuration["DingAuth:appId"];
  367. // string appSecret = BaseConfigModel.Configuration["DingAuth:appSecret"];
  368. string callback = BaseConfigModel.Configuration["DingAuth:callback"];
  369. return builder.Data(new { appId,callback}).build();
  370. }
  371. /// <summary>
  372. /// 初始化登录
  373. /// </summary>
  374. /// <param name="request"></param>
  375. /// <returns></returns>
  376. [HttpPost("init")]
  377. public BaseJosnRPCResponse Init(JosnRPCRequest<string> request)
  378. {
  379. JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom();
  380. if (!string.IsNullOrEmpty(request.@params))
  381. {
  382. var code = securityCode.GetRandomEnDigitalText(4).ToLower();
  383. var imgbyte = securityCode.GetEnDigitalCodeByte(code);
  384. string base64 = "data:image/png;base64," + Convert.ToBase64String(imgbyte);
  385. RedisHelper.HSet("captcha:" + request.@params, request.@params, code);
  386. RedisHelper.Expire("captcha:" + request.@params, smsTTL);
  387. return builder.Data(base64).build();
  388. }
  389. else
  390. {
  391. throw new BizException("随机码为空!", 2);
  392. }
  393. }
  394. /// <summary>
  395. /// 发送短信
  396. /// </summary>
  397. /// <param name="request"></param>
  398. /// <returns></returns>
  399. [HttpPost("sendSMS")]
  400. public async Task<BaseJosnRPCResponse> SendSMS(JosnRPCRequest<CaptchaSms> request)
  401. {
  402. JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom();
  403. string captcha = RedisHelper.HGet<string>("captcha:" + request.@params.randCode, request.@params.randCode);
  404. List<string> RootUsers = BaseConfigModel.Configuration.GetSection("RootUser").Get<List<string>>();
  405. bool f = !string.IsNullOrEmpty(captcha) && captcha.Equals(request.@params.captcha.ToLower());
  406. bool s = RootUsers.Contains(request.@params.cellphone) && request.@params.captcha.ToLower().Equals("0000");
  407. if (f || s)
  408. {
  409. string key = request.@params.cellphone;
  410. if (RedisHelper.Exists(key))
  411. {
  412. string[] vals = RedisHelper.HVals<string>(key);
  413. if (vals != null && vals.Length > 0)
  414. {
  415. Dictionary<string, object> data = new Dictionary<string, object>() { { "msgid", vals[0] }, { "repeat", true } };
  416. return builder.Data(data).build();
  417. }
  418. else
  419. {
  420. return builder.Data(await SendMsg(key)).build();
  421. }
  422. }
  423. else
  424. {
  425. return builder.Data(await SendMsg(key)).build();
  426. }
  427. }
  428. else
  429. {
  430. throw new BizException("验证码错误!", 2);
  431. }
  432. }
  433. private static async Task<Dictionary<string, object>> SendMsg(string key)
  434. {
  435. List<string> RootUsers = BaseConfigModel.Configuration.GetSection("RootUser").Get<List<string>>();
  436. Dictionary<string, object> data = new Dictionary<string, object>() { { "mobile", key }, { "temp_id", 1 }, { "sign_id", "" } };
  437. if (RootUsers.Contains(key))
  438. {
  439. string msgidstr = key;
  440. RedisHelper.Del(new string[] { key });
  441. RedisHelper.HSet(key, key, msgidstr);
  442. RedisHelper.Expire(key, smsTTL);
  443. return new Dictionary<string, object>() { { "msgid", msgidstr }, { "repeat", false } };
  444. }
  445. else
  446. {
  447. string resdata = await HttpClientHelper.Post(
  448. BaseConfigModel.Configuration["JPush:Push"],
  449. BaseConfigModel.Configuration["JPush:AppKey"],
  450. BaseConfigModel.Configuration["JPush:Secret"], data);
  451. JsonElement element = resdata.FromApiJson<JsonElement>();
  452. if (element.TryGetProperty("msg_id", out JsonElement msgid))
  453. {
  454. string msgidstr = msgid.GetString();
  455. RedisHelper.Del(new string[] { key });
  456. RedisHelper.HSet(key, key, msgidstr);
  457. RedisHelper.Expire(key, smsTTL);
  458. return new Dictionary<string, object>() { { "msgid", msgidstr }, { "repeat", false } };
  459. }
  460. else
  461. {
  462. throw new BizException("短信发送失败!", 2);
  463. }
  464. }
  465. }
  466. }
  467. }