AuthController.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. <?php
  2. namespace App\Controllers;
  3. use App\Models\{
  4. User,
  5. Setting,
  6. InviteCode,
  7. EmailVerify
  8. };
  9. use App\Utils\{
  10. GA,
  11. Hash,
  12. Check,
  13. Tools,
  14. TelegramSessionManager
  15. };
  16. use App\Services\{
  17. Auth,
  18. Captcha,
  19. Mail,
  20. Config
  21. };
  22. use voku\helper\AntiXSS;
  23. use Exception;
  24. use Ramsey\Uuid\Uuid;
  25. use Slim\Http\{
  26. Request,
  27. Response
  28. };
  29. /**
  30. * AuthController
  31. */
  32. class AuthController extends BaseController
  33. {
  34. /**
  35. * @param Request $request
  36. * @param Response $response
  37. * @param array $args
  38. */
  39. public function login($request, $response, $args)
  40. {
  41. $captcha = Captcha::generate();
  42. if ($_ENV['enable_telegram_login'] === true) {
  43. $login_text = TelegramSessionManager::add_login_session();
  44. $login = explode('|', $login_text);
  45. $login_token = $login[0];
  46. $login_number = $login[1];
  47. } else {
  48. $login_token = '';
  49. $login_number = '';
  50. }
  51. if (Setting::obtain('enable_login_captcha') == true) {
  52. $geetest_html = $captcha['geetest'];
  53. } else {
  54. $geetest_html = null;
  55. }
  56. return $this->view()
  57. ->assign('geetest_html', $geetest_html)
  58. ->assign('login_token', $login_token)
  59. ->assign('login_number', $login_number)
  60. ->assign('telegram_bot', $_ENV['telegram_bot'])
  61. ->assign('base_url', $_ENV['baseUrl'])
  62. ->assign('recaptcha_sitekey', $captcha['recaptcha'])
  63. ->display('auth/login.tpl');
  64. }
  65. /**
  66. * @param Request $request
  67. * @param Response $response
  68. * @param array $args
  69. */
  70. public function getCaptcha($request, $response, $args)
  71. {
  72. $captcha = Captcha::generate();
  73. return $response->withJson([
  74. 'recaptchaKey' => $captcha['recaptcha'],
  75. 'GtSdk' => $captcha['geetest'],
  76. 'respon' => 1,
  77. ]);
  78. }
  79. /**
  80. * @param Request $request
  81. * @param Response $response
  82. * @param array $args
  83. */
  84. public function loginHandle($request, $response, $args)
  85. {
  86. $email = trim($request->getParam('email'));
  87. $email = strtolower($email);
  88. $passwd = $request->getParam('passwd');
  89. $code = $request->getParam('code');
  90. $rememberMe = $request->getParam('remember_me');
  91. if (Setting::obtain('enable_login_captcha') == true) {
  92. $ret = Captcha::verify($request->getParams());
  93. if (!$ret) {
  94. return $response->withJson([
  95. 'ret' => 0,
  96. 'msg' => '系统无法接受您的验证结果,请刷新页面后重试。'
  97. ]);
  98. }
  99. }
  100. $user = User::where('email', $email)->first();
  101. if ($user == null) {
  102. return $response->withJson([
  103. 'ret' => 0,
  104. 'msg' => '邮箱不存在'
  105. ]);
  106. }
  107. if (!Hash::checkPassword($user->pass, $passwd)) {
  108. // 记录登录失败
  109. $user->collectLoginIP($_SERVER['REMOTE_ADDR'], 1);
  110. return $response->withJson([
  111. 'ret' => 0,
  112. 'msg' => '邮箱或者密码错误'
  113. ]);
  114. }
  115. if ($user->ga_enable == 1) {
  116. $ga = new GA();
  117. $rcode = $ga->verifyCode($user->ga_token, $code);
  118. if (!$rcode) {
  119. return $response->withJson([
  120. 'ret' => 0,
  121. 'msg' => '两步验证码错误,如果您是丢失了生成器或者错误地设置了这个选项,您可以尝试重置密码,即可取消这个选项。'
  122. ]);
  123. }
  124. }
  125. $time = 3600 * 24;
  126. if ($rememberMe) {
  127. $time = 3600 * 24 * ($_ENV['rememberMeDuration'] ?: 7);
  128. }
  129. Auth::login($user->id, $time);
  130. // 记录登录成功
  131. $user->collectLoginIP($_SERVER['REMOTE_ADDR']);
  132. return $response->withJson([
  133. 'ret' => 1,
  134. 'msg' => '登录成功'
  135. ]);
  136. }
  137. /**
  138. * @param Request $request
  139. * @param Response $response
  140. * @param array $args
  141. */
  142. public function qrcode_loginHandle($request, $response, $args)
  143. {
  144. $token = $request->getParam('token');
  145. $number = $request->getParam('number');
  146. $ret = TelegramSessionManager::step2_verify_login_session($token, $number);
  147. if ($ret === 0) {
  148. return $response->withJson([
  149. 'ret' => 0,
  150. 'msg' => '此令牌无法被使用。'
  151. ]);
  152. }
  153. $user = User::find($ret);
  154. Auth::login($user->id, 3600 * 24);
  155. // 记录登录成功
  156. $user->collectLoginIP($_SERVER['REMOTE_ADDR']);
  157. return $response->withJson([
  158. 'ret' => 1,
  159. 'msg' => '登录成功'
  160. ]);
  161. }
  162. /**
  163. * @param Request $request
  164. * @param Response $response
  165. * @param array $args
  166. */
  167. public function register($request, $response, $next)
  168. {
  169. $ary = $request->getQueryParams();
  170. $code = '';
  171. if (isset($ary['code'])) {
  172. $antiXss = new AntiXSS();
  173. $code = $antiXss->xss_clean($ary['code']);
  174. }
  175. $captcha = Captcha::generate();
  176. if ($_ENV['enable_telegram_login'] === true) {
  177. $login_text = TelegramSessionManager::add_login_session();
  178. $login = explode('|', $login_text);
  179. $login_token = $login[0];
  180. $login_number = $login[1];
  181. } else {
  182. $login_token = '';
  183. $login_number = '';
  184. }
  185. if (Setting::obtain('enable_reg_captcha') == true) {
  186. $geetest_html = $captcha['geetest'];
  187. } else {
  188. $geetest_html = null;
  189. }
  190. return $this->view()
  191. ->assign('geetest_html', $geetest_html)
  192. ->assign('enable_email_verify', Setting::obtain('reg_email_verify'))
  193. ->assign('code', $code)
  194. ->assign('recaptcha_sitekey', $captcha['recaptcha'])
  195. ->assign('telegram_bot', $_ENV['telegram_bot'])
  196. ->assign('base_url', $_ENV['baseUrl'])
  197. ->assign('login_token', $login_token)
  198. ->assign('login_number', $login_number)
  199. ->display('auth/register.tpl');
  200. }
  201. /**
  202. * @param Request $request
  203. * @param Response $response
  204. * @param array $args
  205. */
  206. public function sendVerify($request, $response, $next)
  207. {
  208. if (Setting::obtain('reg_email_verify')) {
  209. $email = trim($request->getParam('email'));
  210. $email = strtolower($email);
  211. if ($email == '') {
  212. return $response->withJson([
  213. 'ret' => 0,
  214. 'msg' => '未填写邮箱'
  215. ]);
  216. }
  217. // check email format
  218. $check_res = Check::isEmailLegal($email);
  219. if ($check_res['ret'] == 0) {
  220. return $response->withJson($check_res);
  221. }
  222. $user = User::where('email', $email)->first();
  223. if ($user != null) {
  224. return $response->withJson([
  225. 'ret' => 0,
  226. 'msg' => '此邮箱已经注册'
  227. ]);
  228. }
  229. $ipcount = EmailVerify::where('ip', '=', $_SERVER['REMOTE_ADDR'])->where('expire_in', '>', time())->count();
  230. if ($ipcount >= Setting::obtain('email_verify_ip_limit')) {
  231. return $response->withJson([
  232. 'ret' => 0,
  233. 'msg' => '此IP请求次数过多'
  234. ]);
  235. }
  236. $mailcount = EmailVerify::where('email', '=', $email)->where('expire_in', '>', time())->count();
  237. if ($mailcount >= 3) {
  238. return $response->withJson([
  239. 'ret' => 0,
  240. 'msg' => '此邮箱请求次数过多'
  241. ]);
  242. }
  243. $code = Tools::genRandomNum(6);
  244. $ev = new EmailVerify();
  245. $ev->expire_in = time() + Setting::obtain('email_verify_ttl');
  246. $ev->ip = $_SERVER['REMOTE_ADDR'];
  247. $ev->email = $email;
  248. $ev->code = $code;
  249. $ev->save();
  250. try {
  251. Mail::send(
  252. $email,
  253. $_ENV['appName'] . '- 验证邮件',
  254. 'auth/verify.tpl',
  255. [
  256. 'code' => $code,
  257. 'expire' => date('Y-m-d H:i:s', time() + Setting::obtain('email_verify_ttl'))
  258. ],
  259. []
  260. );
  261. } catch (Exception $e) {
  262. return $response->withJson([
  263. 'ret' => 0,
  264. 'msg' => '邮件发送失败,请联系网站管理员。'
  265. ]);
  266. }
  267. return $response->withJson([
  268. 'ret' => 1,
  269. 'msg' => '验证码发送成功,请查收邮件。'
  270. ]);
  271. }
  272. return $response->withJson([
  273. 'ret' => 0,
  274. 'msg' => ''
  275. ]);
  276. }
  277. /**
  278. * @param Request $request
  279. * @param Response $response
  280. * @param array $args
  281. */
  282. public function register_helper($name, $email, $passwd, $code, $imtype, $imvalue, $telegram_id)
  283. {
  284. if (Setting::obtain('reg_mode') == 'close') {
  285. $res['ret'] = 0;
  286. $res['msg'] = '暂时不对外开放注册';
  287. return $res;
  288. }
  289. if ($code == '') {
  290. $res['ret'] = 0;
  291. $res['msg'] = '注册需要填写邀请码';
  292. return $res;
  293. }
  294. $c = InviteCode::where('code', $code)->first();
  295. if ($c == null) {
  296. if (Setting::obtain('reg_mode') == 'invite') {
  297. $res['ret'] = 0;
  298. $res['msg'] = '这个邀请码不存在';
  299. return $res;
  300. }
  301. } elseif ($c->user_id != 0) {
  302. $gift_user = User::where('id', $c->user_id)->first();
  303. if ($gift_user == null) {
  304. $res['ret'] = 0;
  305. $res['msg'] = '邀请码已失效';
  306. return $res;
  307. }
  308. if ($gift_user->invite_num == 0) {
  309. $res['ret'] = 0;
  310. $res['msg'] = '邀请码不可用';
  311. return $res;
  312. }
  313. }
  314. $configs = Setting::getClass('register');
  315. // do reg user
  316. $user = new User();
  317. $antiXss = new AntiXSS();
  318. $current_timestamp = time();
  319. $user->user_name = $antiXss->xss_clean($name);
  320. $user->email = $email;
  321. $user->pass = Hash::passwordHash($passwd);
  322. $user->passwd = Tools::genRandomChar(16);
  323. $user->uuid = Uuid::uuid3(Uuid::NAMESPACE_DNS, $email . '|' . $current_timestamp);
  324. $user->port = Tools::getAvPort();
  325. $user->t = 0;
  326. $user->u = 0;
  327. $user->d = 0;
  328. $user->method = $configs['sign_up_for_method'];
  329. $user->protocol = $configs['sign_up_for_protocol'];
  330. $user->protocol_param = $configs['sign_up_for_protocol_param'];
  331. $user->obfs = $configs['sign_up_for_obfs'];
  332. $user->obfs_param = $configs['sign_up_for_obfs_param'];
  333. $user->forbidden_ip = $_ENV['reg_forbidden_ip'];
  334. $user->forbidden_port = $_ENV['reg_forbidden_port'];
  335. $user->im_type = $imtype;
  336. $user->im_value = $antiXss->xss_clean($imvalue);
  337. $user->transfer_enable = Tools::toGB($configs['sign_up_for_free_traffic']);
  338. $user->invite_num = $configs['sign_up_for_invitation_codes'];
  339. $user->auto_reset_day = $_ENV['free_user_reset_day'];
  340. $user->auto_reset_bandwidth = $_ENV['free_user_reset_bandwidth'];
  341. $user->money = 0;
  342. $user->sendDailyMail = $configs['sign_up_for_daily_report'];
  343. //dumplin:填写邀请人,写入邀请奖励
  344. $user->ref_by = 0;
  345. if ($c != null && $c->user_id != 0) {
  346. $invitation = Setting::getClass('invite');
  347. // 设置新用户
  348. $user->ref_by = $c->user_id;
  349. $user->money = $invitation['invitation_to_register_balance_reward'];
  350. // 给邀请人反流量
  351. $gift_user->transfer_enable += $invitation['invitation_to_register_traffic_reward'] * 1024 * 1024 * 1024;
  352. if (($gift_user->invite_num - 1) >= 0) {
  353. --$gift_user->invite_num;
  354. // 避免设置为不限制邀请次数的值 -1 发生变动
  355. }
  356. $gift_user->save();
  357. }
  358. if ($telegram_id) {
  359. $user->telegram_id = $telegram_id;
  360. }
  361. $ga = new GA();
  362. $secret = $ga->createSecret();
  363. $user->ga_token = $secret;
  364. $user->ga_enable = 0;
  365. $user->class_expire = date('Y-m-d H:i:s', time() + $configs['sign_up_for_class_time'] * 86400);
  366. $user->class = $configs['sign_up_for_class'];
  367. $user->node_connector = $configs['connection_device_limit'];
  368. $user->node_speedlimit = $configs['connection_rate_limit'];
  369. $user->expire_in = date('Y-m-d H:i:s', time() + $configs['sign_up_for_free_time'] * 86400);
  370. $user->reg_date = date('Y-m-d H:i:s');
  371. $user->reg_ip = $_SERVER['REMOTE_ADDR'];
  372. $user->theme = $_ENV['theme'];
  373. $groups = explode(',', $_ENV['random_group']);
  374. $user->node_group = $groups[array_rand($groups)];
  375. if ($user->save()) {
  376. Auth::login($user->id, 3600);
  377. $user->collectLoginIP($_SERVER['REMOTE_ADDR']);
  378. $res['ret'] = 1;
  379. $res['msg'] = '注册成功!正在进入登录界面';
  380. return $res;
  381. }
  382. $res['ret'] = 0;
  383. $res['msg'] = '未知错误';
  384. return $res;
  385. }
  386. /**
  387. * @param Request $request
  388. * @param Response $response
  389. * @param array $args
  390. */
  391. public function registerHandle($request, $response, $args)
  392. {
  393. if (Setting::obtain('reg_mode') == 'close') {
  394. return $response->withJson([
  395. 'ret' => 0,
  396. 'msg' => '未开放注册。'
  397. ]);
  398. }
  399. $name = $request->getParam('name');
  400. $email = $request->getParam('email');
  401. $email = trim($email);
  402. $email = strtolower($email);
  403. $passwd = $request->getParam('passwd');
  404. $repasswd = $request->getParam('repasswd');
  405. $code = trim($request->getParam('code'));
  406. $emailcode = $request->getParam('emailcode');
  407. $emailcode = trim($emailcode);
  408. if ($_ENV['enable_reg_im'] === true) {
  409. $imtype = $request->getParam('im_type');
  410. $imvalue = $request->getParam('im_value');
  411. if ($imtype == '' || $imvalue == '') {
  412. return $response->withJson([
  413. 'ret' => 0,
  414. 'msg' => '请填上你的联络方式'
  415. ]);
  416. }
  417. $user = User::where('im_value', $imvalue)->where('im_type', $imtype)->first();
  418. if ($user != null) {
  419. return $response->withJson([
  420. 'ret' => 0,
  421. 'msg' => '此联络方式已注册'
  422. ]);
  423. }
  424. } else {
  425. $imtype = 1;
  426. $imvalue = '';
  427. }
  428. if (Setting::obtain('enable_reg_captcha') == true) {
  429. $ret = Captcha::verify($request->getParams());
  430. if (!$ret) {
  431. return $response->withJson([
  432. 'ret' => 0,
  433. 'msg' => '系统无法接受您的验证结果,请刷新页面后重试。'
  434. ]);
  435. }
  436. }
  437. // check email format
  438. $check_res = Check::isEmailLegal($email);
  439. if ($check_res['ret'] == 0) {
  440. return $response->withJson($check_res);
  441. }
  442. // check email
  443. $user = User::where('email', $email)->first();
  444. if ($user != null) {
  445. return $response->withJson([
  446. 'ret' => 0,
  447. 'msg' => '邮箱已经被注册了'
  448. ]);
  449. }
  450. if (Setting::obtain('reg_email_verify')) {
  451. $mailcount = EmailVerify::where('email', '=', $email)->where('code', '=', $emailcode)->where('expire_in', '>', time())->first();
  452. if ($mailcount == null) {
  453. return $response->withJson([
  454. 'ret' => 0,
  455. 'msg' => '您的邮箱验证码不正确'
  456. ]);
  457. }
  458. }
  459. // check pwd length
  460. if (strlen($passwd) < 8) {
  461. return $response->withJson([
  462. 'ret' => 0,
  463. 'msg' => '密码请大于8位'
  464. ]);
  465. }
  466. // check pwd re
  467. if ($passwd != $repasswd) {
  468. return $response->withJson([
  469. 'ret' => 0,
  470. 'msg' => '两次密码输入不符'
  471. ]);
  472. }
  473. if (Setting::obtain('reg_email_verify')) {
  474. EmailVerify::where('email', $email)->delete();
  475. }
  476. return $response->withJson(
  477. $this->register_helper($name, $email, $passwd, $code, $imtype, $imvalue, 0)
  478. );
  479. }
  480. /**
  481. * @param Request $request
  482. * @param Response $response
  483. * @param array $args
  484. */
  485. public function logout($request, $response, $next)
  486. {
  487. Auth::logout();
  488. return $response->withStatus(302)->withHeader('Location', '/auth/login');
  489. }
  490. /**
  491. * @param Request $request
  492. * @param Response $response
  493. * @param array $args
  494. */
  495. public function qrcode_check($request, $response, $args)
  496. {
  497. $token = $request->getParam('token');
  498. $number = $request->getParam('number');
  499. $user = Auth::getUser();
  500. if ($user->isLogin) {
  501. return $response->withJson([
  502. 'ret' => 0
  503. ]);
  504. }
  505. if ($_ENV['enable_telegram_login'] === true) {
  506. $ret = TelegramSessionManager::check_login_session($token, $number);
  507. $res['ret'] = $ret;
  508. return $response->withJson($res);
  509. }
  510. return $response->withJson([
  511. 'ret' => 0
  512. ]);
  513. }
  514. /**
  515. * @param Request $request
  516. * @param Response $response
  517. * @param array $args
  518. */
  519. public function telegram_oauth($request, $response, $args)
  520. {
  521. if ($_ENV['enable_telegram_login'] === true) {
  522. $auth_data = $request->getQueryParams();
  523. if ($this->telegram_oauth_check($auth_data) === true) { // Looks good, proceed.
  524. $telegram_id = $auth_data['id'];
  525. $user = User::query()->where('telegram_id', $telegram_id)->firstOrFail(); // Welcome Back :)
  526. if ($user == null) {
  527. return $this->view()
  528. ->assign('title', '您需要先进行邮箱注册后绑定Telegram才能使用授权登录')
  529. ->assign('message', '很抱歉带来的不便,请重新试试')
  530. ->assign('redirect', '/auth/login')
  531. ->display('telegram_error.tpl');
  532. }
  533. Auth::login($user->id, 3600);
  534. // 记录登录成功
  535. $user->collectLoginIP($_SERVER['REMOTE_ADDR']);
  536. // 登陆成功!
  537. return $this->view()
  538. ->assign('title', '登录成功')
  539. ->assign('message', '正在前往仪表盘')
  540. ->assign('redirect', '/user')
  541. ->display('telegram_success.tpl');
  542. }
  543. // 验证失败
  544. return $this->view()
  545. ->assign('title', '登陆超时或非法构造信息')
  546. ->assign('message', '很抱歉带来的不便,请重新试试')
  547. ->assign('redirect', '/auth/login')
  548. ->display('telegram_error.tpl');
  549. }
  550. return $response->withRedirect('/404');
  551. }
  552. /**
  553. * @param Request $request
  554. * @param Response $response
  555. * @param array $args
  556. */
  557. private function telegram_oauth_check($auth_data)
  558. {
  559. $check_hash = $auth_data['hash'];
  560. $bot_token = $_ENV['telegram_token'];
  561. unset($auth_data['hash']);
  562. $data_check_arr = [];
  563. foreach ($auth_data as $key => $value) {
  564. $data_check_arr[] = $key . '=' . $value;
  565. }
  566. sort($data_check_arr);
  567. $data_check_string = implode("\n", $data_check_arr);
  568. $secret_key = hash('sha256', $bot_token, true);
  569. $hash = hash_hmac('sha256', $data_check_string, $secret_key);
  570. if (strcmp($hash, $check_hash) !== 0) {
  571. return false; // Bad Data :(
  572. }
  573. if ((time() - $auth_data['auth_date']) > 300) { // Expire @ 5mins
  574. return false;
  575. }
  576. return true; // Good to Go
  577. }
  578. }