AuthController.php 19 KB

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