LoginController.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace app\api\controller\v1;
  12. use app\Request;
  13. use app\services\message\notice\SmsService;
  14. use app\services\wechat\WechatServices;
  15. use think\facade\Config;
  16. use crmeb\services\CacheService;
  17. use app\services\user\LoginServices;
  18. use think\exception\ValidateException;
  19. use app\api\validate\user\RegisterValidates;
  20. /**
  21. * 微信小程序授权类
  22. * Class AuthController
  23. * @package app\api\controller
  24. */
  25. class LoginController
  26. {
  27. protected $services;
  28. /**
  29. * LoginController constructor.
  30. * @param LoginServices $services
  31. */
  32. public function __construct(LoginServices $services)
  33. {
  34. $this->services = $services;
  35. }
  36. /**
  37. * H5账号登陆
  38. * @param Request $request
  39. * @return mixed
  40. * @throws \think\db\exception\DataNotFoundException
  41. * @throws \think\db\exception\ModelNotFoundException
  42. * @throws \think\exception\DbException
  43. */
  44. public function login(Request $request)
  45. {
  46. [$account, $password, $spread, $agent_id] = $request->postMore([
  47. 'account', 'password', 'spread', ['agent_id', 0]
  48. ], true);
  49. if (!$account || !$password) {
  50. return app('json')->fail(410000);
  51. }
  52. if (strlen(trim($password)) < 6 || strlen(trim($password)) > 32) {
  53. return app('json')->fail(400762);
  54. }
  55. return app('json')->success(410001, $this->services->login($account, $password, $spread, $agent_id));
  56. }
  57. /**
  58. * 退出登录
  59. * @param Request $request
  60. * @return mixed
  61. */
  62. public function logout(Request $request)
  63. {
  64. $key = trim(ltrim($request->header(Config::get('cookie.token_name')), 'Bearer'));
  65. CacheService::delete(md5($key));
  66. return app('json')->success(410002);
  67. }
  68. /**
  69. * 获取发送验证码key
  70. * @return mixed
  71. */
  72. public function verifyCode()
  73. {
  74. $unique = password_hash(uniqid(true), PASSWORD_BCRYPT);
  75. CacheService::set('sms.key.' . $unique, 0, 300);
  76. $time = sys_config('verify_expire_time', 1);
  77. return app('json')->success(['key' => $unique, 'expire_time' => $time]);
  78. }
  79. /**
  80. * 获取图片验证码
  81. * @param Request $request
  82. * @return \think\Response
  83. */
  84. public function captcha(Request $request)
  85. {
  86. ob_clean();
  87. $rep = captcha();
  88. $key = app('session')->get('captcha.key');
  89. $uni = $request->get('key');
  90. if ($uni) {
  91. CacheService::set('sms.key.cap.' . $uni, $key, 300);
  92. }
  93. return $rep;
  94. }
  95. /**
  96. * 验证验证码是否正确
  97. * @param $uni
  98. * @param string $code
  99. * @return bool
  100. * @throws \Psr\SimpleCache\InvalidArgumentException
  101. */
  102. protected function checkCaptcha($uni, string $code): bool
  103. {
  104. $cacheName = 'sms.key.cap.' . $uni;
  105. if (!CacheService::has($cacheName)) {
  106. return false;
  107. }
  108. $key = CacheService::get($cacheName);
  109. $code = mb_strtolower($code, 'UTF-8');
  110. $res = password_verify($code, $key);
  111. if ($res) {
  112. CacheService::delete($cacheName);
  113. }
  114. return $res;
  115. }
  116. /**
  117. * 验证码发送
  118. * @param Request $request
  119. * @param SmsService $services
  120. * @return mixed
  121. */
  122. public function verify(Request $request, SmsService $services)
  123. {
  124. [$phone, $type, $key, $captchaType, $captchaVerification] = $request->postMore([
  125. ['phone', 0],
  126. ['type', ''],
  127. ['key', ''],
  128. ['captchaType', ''],
  129. ['captchaVerification', ''],
  130. ], true);
  131. $keyName = 'sms.key.' . $key;
  132. if (!CacheService::has($keyName)) return app('json')->fail(410003);
  133. // 验证限制
  134. // 验证码每分钟发送上限
  135. $maxMinuteCountKey = 'sms.minute.' . $phone . date('YmdHi');
  136. $minuteCount = 0;
  137. if (CacheService::has($maxMinuteCountKey)) {
  138. $minuteCount = CacheService::get($maxMinuteCountKey) ?? 0;
  139. $maxMinuteCount = Config::get('sms.maxMinuteCount', 5);
  140. if ($minuteCount > $maxMinuteCount) return app('json')->fail('同一手机号每分钟最多发送' . $maxMinuteCount . '条');
  141. }
  142. // 验证码单个手机每日发送上限
  143. $maxPhoneCountKey = 'sms.phone.' . $phone . '.' . date('Ymd');
  144. $phoneCount = 0;
  145. if (CacheService::has($maxPhoneCountKey)) {
  146. $phoneCount = CacheService::get($maxPhoneCountKey) ?? 0;
  147. $maxPhoneCount = Config::get('sms.maxPhoneCount', 20);
  148. if ($phoneCount > $maxPhoneCount) return app('json')->fail('同一手机号每天最多发送' . $maxPhoneCount . '条');
  149. }
  150. // 验证码单个手机每日发送上限
  151. $maxIpCountKey = 'sms.ip.' . app()->request->ip() . '.' . date('Ymd');
  152. $ipCount = 0;
  153. if (CacheService::has($maxIpCountKey)) {
  154. $ipCount = CacheService::get($maxPhoneCountKey) ?? 0;
  155. $maxIpCount = Config::get('sms.maxIpCount', 50);
  156. if ($ipCount > $maxIpCount) return app('json')->fail('同一IP每天最多发送' . $maxIpCount . '条');
  157. }
  158. //二次验证
  159. try {
  160. aj_captcha_check_two($captchaType, $captchaVerification);
  161. } catch (\Throwable $e) {
  162. return app('json')->fail($e->getError());
  163. }
  164. try {
  165. validate(RegisterValidates::class)->scene('code')->check(['phone' => $phone]);
  166. } catch (ValidateException $e) {
  167. return app('json')->fail($e->getError());
  168. }
  169. $time = sys_config('verify_expire_time', 1);
  170. $smsCode = $this->services->verify($services, $phone, $type, $time);
  171. if ($smsCode) {
  172. CacheService::set('code_' . $phone, $smsCode, $time * 60);
  173. CacheService::set($maxMinuteCountKey, (int)$minuteCount + 1, 61);
  174. CacheService::set($maxPhoneCountKey, (int)$phoneCount + 1, 86401);
  175. CacheService::set($maxIpCountKey, (int)$ipCount + 1, 86401);
  176. return app('json')->success(410007);
  177. } else {
  178. return app('json')->fail(410008);
  179. }
  180. }
  181. /**
  182. * H5注册新用户
  183. * @param Request $request
  184. * @return mixed
  185. * @throws \think\db\exception\DataNotFoundException
  186. * @throws \think\db\exception\DbException
  187. * @throws \think\db\exception\ModelNotFoundException
  188. */
  189. public function register(Request $request)
  190. {
  191. [$account, $captcha, $password, $spread] = $request->postMore([['account', ''], ['captcha', ''], ['password', ''], ['spread', 0]], true);
  192. try {
  193. validate(RegisterValidates::class)->scene('register')->check(['account' => $account, 'captcha' => $captcha, 'password' => $password]);
  194. } catch (ValidateException $e) {
  195. return app('json')->fail($e->getError());
  196. }
  197. if (strlen(trim($password)) < 6 || strlen(trim($password)) > 32) {
  198. return app('json')->fail(400762);
  199. }
  200. $verifyCode = CacheService::get('code_' . $account);
  201. if (!$verifyCode)
  202. return app('json')->fail(410009);
  203. $verifyCode = substr($verifyCode, 0, 6);
  204. if ($verifyCode != $captcha)
  205. return app('json')->fail(410010);
  206. if (md5($password) == md5('123456')) return app('json')->fail(410012);
  207. $registerStatus = $this->services->register($account, $password, $spread, 'h5');
  208. if ($registerStatus) {
  209. return app('json')->success(410013);
  210. }
  211. return app('json')->fail(410014);
  212. }
  213. /**
  214. * 密码修改
  215. * @param Request $request
  216. * @return mixed
  217. * @throws \think\db\exception\DataNotFoundException
  218. * @throws \think\db\exception\DbException
  219. * @throws \think\db\exception\ModelNotFoundException
  220. */
  221. public function reset(Request $request)
  222. {
  223. [$account, $captcha, $password] = $request->postMore([['account', ''], ['captcha', ''], ['password', '']], true);
  224. try {
  225. validate(RegisterValidates::class)->scene('register')->check(['account' => $account, 'captcha' => $captcha, 'password' => $password]);
  226. } catch (ValidateException $e) {
  227. return app('json')->fail($e->getError());
  228. }
  229. if (strlen(trim($password)) < 6 || strlen(trim($password)) > 32) {
  230. return app('json')->fail(400762);
  231. }
  232. $verifyCode = CacheService::get('code_' . $account);
  233. if (!$verifyCode)
  234. return app('json')->fail(410009);
  235. $verifyCode = substr($verifyCode, 0, 6);
  236. if ($verifyCode != $captcha) {
  237. return app('json')->fail(410010);
  238. }
  239. if ($password == '123456') return app('json')->fail(410012);
  240. $resetStatus = $this->services->reset($account, $password);
  241. if ($resetStatus) return app('json')->success(100001);
  242. return app('json')->fail(100007);
  243. }
  244. /**
  245. * 手机号登录
  246. * @param Request $request
  247. * @return mixed
  248. * @throws \think\db\exception\DataNotFoundException
  249. * @throws \think\db\exception\ModelNotFoundException
  250. * @throws \think\exception\DbException
  251. */
  252. public function mobile(Request $request)
  253. {
  254. [$phone, $captcha, $spread, $agent_id] = $request->postMore([['phone', ''], ['captcha', ''], ['spread', 0], ['agent_id', 0]], true);
  255. //验证手机号
  256. try {
  257. validate(RegisterValidates::class)->scene('code')->check(['phone' => $phone]);
  258. } catch (ValidateException $e) {
  259. return app('json')->fail($e->getError());
  260. }
  261. //验证验证码
  262. $verifyCode = CacheService::get('code_' . $phone);
  263. if (!$verifyCode)
  264. return app('json')->fail(410009);
  265. $verifyCode = substr($verifyCode, 0, 6);
  266. if ($verifyCode != $captcha) {
  267. return app('json')->fail(410010);
  268. }
  269. $user_type = $request->getFromType() ? $request->getFromType() : 'h5';
  270. $token = $this->services->mobile($phone, $spread, $user_type, $agent_id);
  271. if ($token) {
  272. CacheService::delete('code_' . $phone);
  273. return app('json')->success(410001, $token);
  274. } else {
  275. return app('json')->fail(410002);
  276. }
  277. }
  278. /**
  279. * H5切换登陆
  280. * @param Request $request
  281. * @return mixed
  282. * @throws \think\db\exception\DataNotFoundException
  283. * @throws \think\db\exception\ModelNotFoundException
  284. * @throws \think\exception\DbException
  285. */
  286. public function switch_h5(Request $request)
  287. {
  288. $from = $request->post('from', 'wechat');
  289. $user = $request->user();
  290. $token = $this->services->switchAccount($user, $from);
  291. if ($token) {
  292. $token['userInfo'] = $user;
  293. return app('json')->success(410001, $token);
  294. } else
  295. return app('json')->fail(410002);
  296. }
  297. /**
  298. * 绑定手机号
  299. * @param Request $request
  300. * @return mixed
  301. * @throws \think\db\exception\DataNotFoundException
  302. * @throws \think\db\exception\ModelNotFoundException
  303. * @throws \think\exception\DbException
  304. */
  305. public function binding_phone(Request $request)
  306. {
  307. list($phone, $captcha, $key) = $request->postMore([
  308. ['phone', ''],
  309. ['captcha', ''],
  310. ['key', '']
  311. ], true);
  312. //验证手机号
  313. try {
  314. validate(RegisterValidates::class)->scene('code')->check(['phone' => $phone]);
  315. } catch (ValidateException $e) {
  316. return app('json')->fail($e->getError());
  317. }
  318. if (!$key) {
  319. return app('json')->fail(100100);
  320. }
  321. if (!$phone) {
  322. return app('json')->fail(410015);
  323. }
  324. //验证验证码
  325. $verifyCode = CacheService::get('code_' . $phone);
  326. if (!$verifyCode)
  327. return app('json')->fail(410009);
  328. $verifyCode = substr($verifyCode, 0, 6);
  329. if ($verifyCode != $captcha) {
  330. return app('json')->fail(410010);
  331. }
  332. $re = $this->services->bindind_phone($phone, $key);
  333. if ($re) {
  334. CacheService::delete('code_' . $phone);
  335. return app('json')->success(410016, $re);
  336. } else
  337. return app('json')->fail(410017);
  338. }
  339. /**
  340. * 绑定手机号
  341. * @param Request $request
  342. * @return mixed
  343. * @throws \think\db\exception\DataNotFoundException
  344. * @throws \think\db\exception\ModelNotFoundException
  345. * @throws \think\exception\DbException
  346. */
  347. public function user_binding_phone(Request $request)
  348. {
  349. list($phone, $captcha, $step) = $request->postMore([
  350. ['phone', ''],
  351. ['captcha', ''],
  352. ['step', 0]
  353. ], true);
  354. //验证手机号
  355. try {
  356. validate(RegisterValidates::class)->scene('code')->check(['phone' => $phone]);
  357. } catch (ValidateException $e) {
  358. return app('json')->fail($e->getError());
  359. }
  360. if (!$step) {
  361. //验证验证码
  362. $verifyCode = CacheService::get('code_' . $phone);
  363. if (!$verifyCode)
  364. return app('json')->fail(410009);
  365. $verifyCode = substr($verifyCode, 0, 6);
  366. if ($verifyCode != $captcha)
  367. return app('json')->fail(410010);
  368. }
  369. $uid = (int)$request->uid();
  370. $re = $this->services->userBindindPhone($uid, $phone, $step);
  371. if ($re) {
  372. CacheService::delete('code_' . $phone);
  373. return app('json')->success($re['msg'] ?? 410016, $re['data'] ?? []);
  374. } else
  375. return app('json')->fail(410017);
  376. }
  377. public function update_binding_phone(Request $request)
  378. {
  379. [$phone, $captcha] = $request->postMore([
  380. ['phone', ''],
  381. ['captcha', ''],
  382. ], true);
  383. //验证手机号
  384. try {
  385. validate(RegisterValidates::class)->scene('code')->check(['phone' => $phone]);
  386. } catch (ValidateException $e) {
  387. return app('json')->fail($e->getError());
  388. }
  389. //验证验证码
  390. $verifyCode = CacheService::get('code_' . $phone);
  391. if (!$verifyCode)
  392. return app('json')->fail(410009);
  393. $verifyCode = substr($verifyCode, 0, 6);
  394. if ($verifyCode != $captcha)
  395. return app('json')->fail(410010);
  396. $uid = (int)$request->uid();
  397. $re = $this->services->updateBindindPhone($uid, $phone);
  398. if ($re) {
  399. CacheService::delete('code_' . $phone);
  400. return app('json')->success($re['msg'] ?? 100001, $re['data'] ?? []);
  401. } else
  402. return app('json')->fail(100007);
  403. }
  404. /**
  405. * 设置扫描二维码状态
  406. * @param string $code
  407. * @return mixed
  408. */
  409. public function setLoginKey(string $code)
  410. {
  411. if (!$code) {
  412. return app('json')->fail(410020);
  413. }
  414. $cacheCode = CacheService::get($code);
  415. if ($cacheCode === false || $cacheCode === null) {
  416. return app('json')->fail(410021);
  417. }
  418. CacheService::set($code, '0', 600);
  419. return app('json')->success();
  420. }
  421. /**
  422. * apple快捷登陆
  423. * @param Request $request
  424. * @param WechatServices $services
  425. * @return mixed
  426. * @throws \Psr\SimpleCache\InvalidArgumentException
  427. * @throws \think\db\exception\DataNotFoundException
  428. * @throws \think\db\exception\ModelNotFoundException
  429. */
  430. public function appleLogin(Request $request, WechatServices $services)
  431. {
  432. [$openId, $phone, $email, $captcha] = $request->postMore([
  433. ['openId', ''],
  434. ['phone', ''],
  435. ['email', ''],
  436. ['captcha', '']
  437. ], true);
  438. if ($phone) {
  439. if (!$captcha) {
  440. return app('json')->fail(410004);
  441. }
  442. //验证验证码
  443. $verifyCode = CacheService::get('code_' . $phone);
  444. if (!$verifyCode)
  445. return app('json')->fail(410009);
  446. $verifyCode = substr($verifyCode, 0, 6);
  447. if ($verifyCode != $captcha) {
  448. CacheService::delete('code_' . $phone);
  449. return app('json')->fail(410010);
  450. }
  451. }
  452. if ($email == '') $email = substr(md5($openId), 0, 12);
  453. $userInfo = [
  454. 'openId' => $openId,
  455. 'unionid' => '',
  456. 'avatarUrl' => sys_config('h5_avatar'),
  457. 'nickName' => $email,
  458. ];
  459. $token = $services->appAuth($userInfo, $phone, 'apple');
  460. if ($token) {
  461. return app('json')->success(410001, $token);
  462. } else if ($token === false) {
  463. return app('json')->success(410001, ['isbind' => true]);
  464. } else {
  465. return app('json')->fail(410019);
  466. }
  467. }
  468. /**
  469. * 滑块验证
  470. * @return mixed
  471. */
  472. public function ajcaptcha(Request $request)
  473. {
  474. $captchaType = $request->get('captchaType');
  475. return app('json')->success(aj_captcha_create($captchaType));
  476. }
  477. /**
  478. * 一次验证
  479. * @return mixed
  480. */
  481. public function ajcheck(Request $request)
  482. {
  483. [$token, $pointJson, $captchaType] = $request->postMore([
  484. ['token', ''],
  485. ['pointJson', ''],
  486. ['captchaType', ''],
  487. ], true);
  488. try {
  489. aj_captcha_check_one($captchaType, $token, $pointJson);
  490. return app('json')->success();
  491. } catch (\Throwable $e) {
  492. return app('json')->fail(400336);
  493. }
  494. }
  495. /**
  496. * 远程登录接口
  497. * @param Request $request
  498. * @return \think\Response
  499. * @throws \Psr\SimpleCache\InvalidArgumentException
  500. * @throws \think\db\exception\DataNotFoundException
  501. * @throws \think\db\exception\DbException
  502. * @throws \think\db\exception\ModelNotFoundException
  503. * @author wuhaotian
  504. * @email 442384644@qq.com
  505. * @date 2024/5/21
  506. */
  507. public function remoteRegister(Request $request)
  508. {
  509. [$remote_token] = $request->getMore([
  510. ['remote_token', ''],
  511. ], true);
  512. if ($remote_token == '') return app('json')->success('登录失败', ['get_remote_login_url' => sys_config('get_remote_login_url')]);
  513. return app('json')->success('登录成功', $this->services->remoteRegister($remote_token));
  514. }
  515. }