Auth.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. <?php
  2. namespace app\common\library;
  3. use app\common\model\User;
  4. use app\common\model\UserThird;
  5. use fast\Random;
  6. use fast\ucenter\client\Client;
  7. use think\Cookie;
  8. use think\Db;
  9. use think\Exception;
  10. use think\Validate;
  11. /**
  12. * Auth类
  13. */
  14. class Auth
  15. {
  16. const ERR_ACCOUNT_IS_INCORRECT = 'Account is incorrect';
  17. const ERR_ACCOUNT_NOT_EXIST = 'Account not exist';
  18. const ERR_USERNAME_IS_INCORRECT = 'Username is incorrect';
  19. const ERR_EMAIL_IS_INCORRECT = 'Email is incorrect';
  20. const ERR_PASSWORD_IS_INCORRECT = 'Password is incorrect';
  21. const ERR_USERNAME_OR_PASSWORD_IS_INCORRECT = 'Username or password is incorrect';
  22. const ERR_USERNAME_ALREADY_EXIST = 'Username already exist';
  23. const ERR_EMAIL_ALREADY_EXIST = 'Email already exist';
  24. const ERR_MOBILE_ALREADY_EXIST = 'Mobile already exist';
  25. const ERR_ACCOUNT_IS_LOCKED = 'Account is locked';
  26. const ERR_USERNAME_OR_PASSWORD_IS_MODIFIED = 'Username or password is modified';
  27. const ERR_YOU_ARE_NOT_LOGGED_IN = 'You are not logged in';
  28. const ERR_ACCOUNT_ALREADY_LOGGED_IN_AT_ANOTHER_PLACE = 'Account already logged in at another';
  29. protected static $instance = null;
  30. private $_error = '';
  31. private $_logined = FALSE;
  32. private $user = NULL;
  33. private $keeptime = 0;
  34. private $requestUri = '';
  35. public function __construct()
  36. {
  37. $this->user = new User;
  38. }
  39. /**
  40. * 初始化
  41. * @param array $options 参数
  42. * @return Auth
  43. */
  44. public static function instance($options = [])
  45. {
  46. if (is_null(self::$instance))
  47. {
  48. self::$instance = new static($options);
  49. }
  50. return self::$instance;
  51. }
  52. public function __get($name)
  53. {
  54. return $this->check() ? $this->user->$name : NULL;
  55. }
  56. public function __call($name, $arguments)
  57. {
  58. return call_user_func_array([$this->user, $name], $arguments);
  59. }
  60. /**
  61. * 注册用户
  62. *
  63. * @param string $username 用户名
  64. * @param string $password 密码
  65. * @param string $email 邮箱
  66. * @param string $mobile 手机号
  67. * @param string $extend 扩展参数
  68. * @return boolean
  69. */
  70. public function register($username, $password, $email = '', $mobile = '', $extend = [], $keeptime = 0, $sync = TRUE)
  71. {
  72. $rule = [
  73. 'username' => 'require|length:6,30',
  74. 'password' => 'require|length:6,30',
  75. 'email' => 'email',
  76. 'mobile' => 'regex:/^1\d{10}$/',
  77. ];
  78. $msg = [
  79. 'username.require' => __('Username can not be empty'),
  80. 'username.length' => __('Username must be 6 to 30 characters'),
  81. 'password.require' => __('Password can not be empty'),
  82. 'password.length' => __('Password must be 6 to 30 characters'),
  83. 'email' => __('Email is incorrect'),
  84. 'mobile' => __('Mobile is incorrect'),
  85. ];
  86. $data = [
  87. 'username' => $username,
  88. 'password' => $password,
  89. 'email' => $email,
  90. 'mobile' => $mobile,
  91. ];
  92. $validate = new Validate($rule, $msg);
  93. $result = $validate->check($data);
  94. if (!$result)
  95. {
  96. $this->setError($validate->getError());
  97. return FALSE;
  98. }
  99. // 检测用户名或邮箱、手机号是否存在
  100. if (User::getByUsername($username))
  101. {
  102. $this->setError(__('Username already exist'));
  103. return FALSE;
  104. }
  105. if ($email && User::getByEmail($email))
  106. {
  107. $this->setError(__('Email already exist'));
  108. return FALSE;
  109. }
  110. if ($mobile && User::getByMobile($mobile))
  111. {
  112. $this->setError(__('Mobile already exist'));
  113. return FALSE;
  114. }
  115. $ip = request()->ip();
  116. $time = time();
  117. $params = array_merge($data, [
  118. 'nickname' => $username,
  119. 'salt' => Random::alnum(),
  120. 'jointime' => $time,
  121. 'joinip' => $ip,
  122. 'logintime' => $time,
  123. 'loginip' => $ip,
  124. 'prevtime' => $time,
  125. 'status' => 'normal'
  126. ]);
  127. $params['password'] = $this->getEncryptPassword($password, $params['salt']);
  128. $params = array_merge($params, $extend);
  129. ////////////////同步到Ucenter////////////////
  130. if (defined('UC_STATUS') && UC_STATUS && $sync)
  131. {
  132. $uc = new Client();
  133. $user_id = $uc->uc_user_register($username, $password, $email);
  134. // 如果小于0则说明发生错误
  135. if ($user_id <= 0)
  136. {
  137. $this->setError($user_id > -4 ? self::ERR_USERNAME_IS_INCORRECT : self::ERR_EMAIL_IS_INCORRECT);
  138. return FALSE;
  139. }
  140. else
  141. {
  142. $params['id'] = $user_id;
  143. }
  144. }
  145. //账号注册时需要开启事务,避免出现垃圾数据
  146. Db::startTrans();
  147. try
  148. {
  149. $ret = $this->user->save($params);
  150. Db::commit();
  151. // 此时的Model中只包含部分数据
  152. $this->user = $this->user->get($this->user->id);
  153. $this->keeptime($keeptime);
  154. return $this->syncLogin();
  155. }
  156. catch (Exception $e)
  157. {
  158. Db::rollback();
  159. return FALSE;
  160. }
  161. }
  162. /**
  163. * 用户登录
  164. *
  165. * @param string $account 账号,用户名、邮箱、手机号
  166. * @param string $password 密码
  167. * @param int $keeptime 有效时长,默认为浏览器关闭
  168. * @return array
  169. */
  170. public function login($account, $password, $keeptime = 0, $sync = TRUE)
  171. {
  172. $field = Validate::is($account, 'email') ? 'email' : (Validate::regex($account, '/^1\d{10}$/') ? 'mobile' : 'username');
  173. $user = $this->user->get([$field => $account]);
  174. if ($user)
  175. {
  176. if ($user->status != 'normal')
  177. {
  178. $this->setError(self::ERR_ACCOUNT_IS_LOCKED);
  179. return FALSE;
  180. }
  181. if ($user->password != $this->getEncryptPassword($password, $user->salt))
  182. {
  183. $this->setError(self::ERR_PASSWORD_IS_INCORRECT);
  184. return FALSE;
  185. }
  186. $this->user = $user;
  187. // 设置登录有效时长
  188. $this->keeptime($keeptime);
  189. return $this->syncLogin($sync);
  190. }
  191. else
  192. {
  193. $this->setError(self::ERR_ACCOUNT_IS_INCORRECT);
  194. return FALSE;
  195. }
  196. }
  197. /**
  198. * 注销登录退出
  199. * @return bool
  200. */
  201. public function logout($token = NULL)
  202. {
  203. //设置登录标识
  204. $this->_logined = FALSE;
  205. $token = is_null($token) ? Cookie::get('token') : $token;
  206. Token::delete($token);
  207. Cookie::delete('user_id');
  208. //Cookie::del('username');
  209. Cookie::delete('token');
  210. return TRUE;
  211. }
  212. /**
  213. * 生成Token
  214. * @return string
  215. */
  216. public function token()
  217. {
  218. //$token = Encrypt::aesEncode($this->keeptime . '|' . $expiretime, Config::get('encrypt', 'aes_key'), TRUE);
  219. $token = Random::uuid();
  220. Token::set($token, $this->user->id, $this->keeptime);
  221. return $token;
  222. }
  223. /**
  224. * 初始化
  225. *
  226. * @param int $user_id 会员ID,默认从Cookie中取
  227. * @param string $token 会员Token,默认从Cookie中取
  228. *
  229. * @return boolean
  230. */
  231. public function init($user_id = NULL, $token = NULL)
  232. {
  233. $user_id = $user_id ? $user_id : Cookie::get('user_id');
  234. $user_id = intval($user_id);
  235. if ($user_id > 0)
  236. {
  237. if ($this->_error)
  238. return FALSE;
  239. $user = $this->get($user_id);
  240. if (!$user)
  241. {
  242. $this->setError(self::ERR_ACCOUNT_NOT_EXIST);
  243. return FALSE;
  244. }
  245. if ($user['status'] != 'normal')
  246. {
  247. $this->setError(self::ERR_ACCOUNT_IS_LOCKED);
  248. return FALSE;
  249. }
  250. $token = $token ? $token : Cookie::get('token');
  251. if (!Token::check($token))
  252. {
  253. return FALSE;
  254. }
  255. $this->user = $user;
  256. $this->_logined = TRUE;
  257. return TRUE;
  258. }
  259. else
  260. {
  261. $this->setError(self::ERR_YOU_ARE_NOT_LOGGED_IN);
  262. return FALSE;
  263. }
  264. }
  265. /**
  266. * 检测是否登录
  267. *
  268. * @return boolean
  269. */
  270. public function check()
  271. {
  272. return $this->_logined;
  273. }
  274. /**
  275. * 检测是否登录
  276. *
  277. * @return boolean
  278. */
  279. public function isLogin()
  280. {
  281. return $this->check();
  282. }
  283. /**
  284. * 获取当前请求的URI
  285. * @return string
  286. */
  287. public function getRequestUri()
  288. {
  289. return $this->requestUri;
  290. }
  291. /**
  292. * 设置当前请求的URI
  293. * @param string $uri
  294. */
  295. public function setRequestUri($uri)
  296. {
  297. $this->requestUri = $uri;
  298. }
  299. /**
  300. * 第三方登录
  301. * @param string $platform
  302. * @param array $params
  303. * @param int $keeptime
  304. * @return boolean
  305. */
  306. public function connect($platform, $params = [], $keeptime = 0)
  307. {
  308. $time = time();
  309. $values = [
  310. 'platform' => $platform,
  311. 'openid' => $params['openid'],
  312. 'openname' => isset($params['userinfo']['nickname']) ? $params['userinfo']['nickname'] : '',
  313. 'access_token' => $params['access_token'],
  314. 'refresh_token' => $params['refresh_token'],
  315. 'expires_in' => $params['expires_in'],
  316. 'logintime' => $time,
  317. 'expiretime' => $time + $params['expires_in'],
  318. ];
  319. $this->keeptime($keeptime);
  320. $userthird = UserThird::get(['platform' => $platform, 'openid' => $params['openid']]);
  321. if ($userthird)
  322. {
  323. $this->user = $this->user->get($userthird['user_id']);
  324. if (!$this->user)
  325. {
  326. return FALSE;
  327. }
  328. $userthird->save($values);
  329. return $this->syncLogin();
  330. }
  331. else
  332. {
  333. // 先随机一个用户名,随后再变更为u+数字id
  334. $username = Random::alnum(20);
  335. $password = Random::alnum(6);
  336. // 默认注册一个会员
  337. $result = $this->register($username, $password, '', '', [], $keeptime);
  338. if (!$result)
  339. {
  340. return FALSE;
  341. }
  342. $userarr = ['username' => 'u' . $this->user->id];
  343. if (isset($params['userinfo']['nickname']))
  344. $userarr['nickname'] = $params['userinfo']['nickname'];
  345. if (isset($params['userinfo']['avatar']))
  346. $userarr['avatar'] = $params['userinfo']['avatar'];
  347. // 更新会员资料
  348. $this->user->save($userarr);
  349. // 保存第三方信息
  350. $values['user_id'] = $this->user->id;
  351. UserThird::create($values);
  352. // 写入登录Cookies和Token
  353. $this->writeStatus();
  354. return TRUE;
  355. }
  356. }
  357. /**
  358. * 删除一个指定会员
  359. * @param int $user_id
  360. * @param bool $sync 是否同步删除
  361. */
  362. public function delete($user_id, $sync = TRUE)
  363. {
  364. $user = $this->user->get($user_id);
  365. if (!$user)
  366. {
  367. return FALSE;
  368. }
  369. ////////////////同步到Ucenter////////////////
  370. if (defined('UC_STATUS') && UC_STATUS && $sync)
  371. {
  372. $uc = new Client();
  373. $re = $uc->uc_user_delete($user['id']);
  374. // 如果小于0则说明发生错误
  375. if ($re <= 0)
  376. {
  377. $this->setError(self::ERR_ACCOUNT_IS_LOCKED);
  378. return FALSE;
  379. }
  380. }
  381. // 调用事务删除账号
  382. $result = Db::transaction(function($db) use($user_id)
  383. {
  384. // 删除会员
  385. User::destroy($user_id);
  386. // 删除会员第三方登录
  387. UserThird::destroy($user_id);
  388. });
  389. return $result ? TRUE : FALSE;
  390. }
  391. /**
  392. * 直接登录账号
  393. * @param int $user_id
  394. * @param boolean $sync
  395. * @return boolean
  396. */
  397. public function direct($user_id, $sync = TRUE)
  398. {
  399. $this->user = $this->user->get($user_id);
  400. if ($this->user)
  401. {
  402. $this->syncLogin($sync);
  403. return TRUE;
  404. }
  405. else
  406. {
  407. return FALSE;
  408. }
  409. }
  410. /**
  411. * 获取密码加密方式
  412. * @param string $password
  413. * @param string $salt
  414. * @return string
  415. */
  416. public function getEncryptPassword($password, $salt = '')
  417. {
  418. return md5(md5($password) . $salt);
  419. }
  420. /**
  421. * 同步登录信息
  422. * @param int $sync 是否同步登录到UC
  423. * @return boolean
  424. */
  425. protected function syncLogin($sync = TRUE)
  426. {
  427. ////////////////同步到Ucenter////////////////
  428. if (defined('UC_STATUS') && UC_STATUS && $sync)
  429. {
  430. $uc = new Client();
  431. $re = $uc->uc_user_login($this->user->id, $this->user->password . '#split#' . $this->user->salt, 3);
  432. // 如果小于0则说明发生错误
  433. if ($re <= 0)
  434. {
  435. $this->setError(self::ERR_USERNAME_OR_PASSWORD_IS_INCORRECT);
  436. return FALSE;
  437. }
  438. }
  439. //增加登录次数和设置最后登录时间
  440. $this->user->save([
  441. 'prevtime' => $this->user->logintime,
  442. 'logintime' => time(),
  443. 'loginip' => request()->ip(),
  444. ]);
  445. // 写入登录Cookies和Token
  446. $this->writeStatus();
  447. return TRUE;
  448. }
  449. /**
  450. * 写入登录态和Cookie
  451. *
  452. * @param int $keeptime
  453. */
  454. protected function writeStatus()
  455. {
  456. //设置登录标识
  457. $this->_logined = TRUE;
  458. $token = $this->token();
  459. Cookie::set('user_id', $this->user->id, $this->keeptime);
  460. Cookie::set('username', $this->user->username, 86400 * 365);
  461. //加密安全字符
  462. Cookie::set('token', $token, $this->keeptime);
  463. $this->setError('');
  464. }
  465. /**
  466. * 设置会话有效时间
  467. * @param int $keeptime 默认为永久
  468. */
  469. public function keeptime($keeptime = 0)
  470. {
  471. $this->keeptime = $keeptime;
  472. }
  473. /**
  474. * 渲染用户数据
  475. * @param array $datalist
  476. * @param array $fields
  477. * @param string $fieldkey
  478. * @param string $renderkey
  479. * @return array
  480. */
  481. public function render(&$datalist, $fields = [], $fieldkey = 'user_id', $renderkey = 'userinfo')
  482. {
  483. $fields = !$fields ? ['id', 'nickname', 'level', 'avatar'] : (is_array($fields) ? $fields : explode(',', $fields));
  484. $ids = [];
  485. foreach ($datalist as $k => $v)
  486. {
  487. if (!isset($v[$fieldkey]))
  488. continue;
  489. $ids[] = $v[$fieldkey];
  490. }
  491. $list = [];
  492. if ($ids)
  493. {
  494. if (!in_array('id', $fields))
  495. {
  496. $fields[] = 'id';
  497. }
  498. $ids = array_unique($ids);
  499. $selectlist = User::where('id', 'in', $ids)->column($fields);
  500. foreach ($selectlist as $k => $v)
  501. {
  502. $list[$v['id']] = $v;
  503. }
  504. }
  505. foreach ($datalist as $k => &$v)
  506. {
  507. $v[$renderkey] = isset($list[$v[$fieldkey]]) ? $list[$v[$fieldkey]] : NULL;
  508. }
  509. unset($v);
  510. return $datalist;
  511. }
  512. /**
  513. * 设置错误信息
  514. *
  515. * @param $error
  516. */
  517. public function setError($error)
  518. {
  519. $this->_error = $error;
  520. return $this;
  521. }
  522. /**
  523. * 获取错误信息
  524. * @return string
  525. */
  526. public function getError()
  527. {
  528. return __($this->_error);
  529. }
  530. }