Auth.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. <?php
  2. namespace app\common\library;
  3. use ba\Random;
  4. use think\Exception;
  5. use think\facade\Db;
  6. use think\facade\Event;
  7. use think\facade\Config;
  8. use app\common\model\User;
  9. use think\facade\Validate;
  10. use app\common\facade\Token;
  11. use think\db\exception\DbException;
  12. use think\db\exception\PDOException;
  13. use think\db\exception\DataNotFoundException;
  14. use think\db\exception\ModelNotFoundException;
  15. /**
  16. * 公共权限类(会员权限类)
  17. */
  18. class Auth extends \ba\Auth
  19. {
  20. /**
  21. * @var Auth 对象实例
  22. */
  23. protected static $instance;
  24. /**
  25. * @var bool 是否登录
  26. */
  27. protected $logined = false;
  28. /**
  29. * @var string 错误消息
  30. */
  31. protected $error = '';
  32. /**
  33. * @var User Model实例
  34. */
  35. protected $model = null;
  36. /**
  37. * @var string 令牌
  38. */
  39. protected $token = '';
  40. /**
  41. * @var string 刷新令牌
  42. */
  43. protected $refreshToken = '';
  44. /**
  45. * @var int 令牌默认有效期
  46. */
  47. protected $keeptime = 86400;
  48. /**
  49. * @var string[] 允许输出的字段
  50. */
  51. protected $allowFields = [
  52. 'id', 'group_id', 'username', 'nickname', 'email', 'mobile', 'real_name', 'real_name_status', 'identity', 'identity_img', 'certified', 'license_type', 'pilot_license', 'city', 'address', 'departure_point', 'planned_range', 'planned_height', 'aircraft_sn', 'aircraft_brand', 'aircraft_img', 'staff', 'staff_license', 'start_planning', 'end_planning', 'avatar', 'gender', 'birthday', 'money', 'score', 'jointime', 'motto', 'lastlogintime', 'lastloginip', 'business_id', 'business_license_no', 'business_license_img', 'business_certified_img'];
  53. public function __construct(array $config = [])
  54. {
  55. parent::__construct(array_merge([
  56. 'auth_group' => 'user_group', // 用户组数据表名
  57. 'auth_group_access' => '', // 用户-用户组关系表(关系字段)
  58. 'auth_rule' => 'user_rule', // 权限规则表
  59. ], $config));
  60. }
  61. /**
  62. * 魔术方法-会员信息字段
  63. * @param $name
  64. * @return null|string 字段信息
  65. */
  66. public function __get($name)
  67. {
  68. return $this->model ? $this->model->$name : null;
  69. }
  70. /**
  71. * 初始化
  72. * @access public
  73. * @param array $options 参数
  74. * @return Auth
  75. */
  76. public static function instance(array $options = []): Auth
  77. {
  78. if (is_null(self::$instance)) {
  79. self::$instance = new static($options);
  80. }
  81. return self::$instance;
  82. }
  83. /**
  84. * 根据Token初始化会员登录态
  85. * @param $token
  86. * @return bool
  87. * @throws DataNotFoundException
  88. * @throws DbException
  89. * @throws ModelNotFoundException
  90. */
  91. public function init($token): bool
  92. {
  93. if ($this->logined) {
  94. return true;
  95. }
  96. if ($this->error) {
  97. return false;
  98. }
  99. $tokenData = Token::get($token);
  100. if (!$tokenData) {
  101. return false;
  102. }
  103. $userId = intval($tokenData['user_id']);
  104. if ($tokenData['type'] == 'user' && $userId > 0) {
  105. $this->model = User::where('id', $userId)->find();
  106. if (!$this->model) {
  107. $this->setError('Account not exist');
  108. return false;
  109. }
  110. if ($this->model['status'] != 'enable') {
  111. $this->setError('Account disabled');
  112. return false;
  113. }
  114. $this->token = $token;
  115. $this->loginSuccessful();
  116. return true;
  117. } else {
  118. $this->setError('Token login failed');
  119. return false;
  120. }
  121. }
  122. /**
  123. * 会员注册
  124. * @param string $username
  125. * @param string $password
  126. * @param string $mobile
  127. * @param string $email
  128. * @param int $group
  129. * @param array $extend
  130. * @return bool
  131. */
  132. public function register(string $username, string $password, string $mobile = '', string $email = '', int $group = 1, array $extend = []): bool
  133. {
  134. $validate = Validate::rule([
  135. 'mobile' => 'mobile|unique:user',
  136. 'email' => 'email|unique:user',
  137. 'username' => 'regex:^[a-zA-Z][a-zA-Z0-9_]{2,15}$|unique:user',
  138. 'password' => 'regex:^(?!.*[&<>"\'\n\r]).{6,32}$',
  139. ]);
  140. $params = [
  141. 'username' => $username,
  142. 'password' => $password,
  143. 'mobile' => $mobile,
  144. 'email' => $email,
  145. ];
  146. if (!$validate->check($params)) {
  147. $this->setError('Registration parameter error');
  148. return false;
  149. }
  150. $ip = request()->ip();
  151. $time = time();
  152. $salt = Random::build('alnum', 16);
  153. $data = [
  154. 'password' => encrypt_password($password, $salt),
  155. 'group_id' => $group,
  156. 'nickname' => preg_match("/^1[3-9]\d{9}$/", $username) ? substr_replace($username, '****', 3, 4) : $username,
  157. 'joinip' => $ip,
  158. 'jointime' => $time,
  159. 'lastloginip' => $ip,
  160. 'lastlogintime' => $time,
  161. 'salt' => $salt,
  162. 'status' => 'enable',
  163. ];
  164. $data = array_merge($params, $data);
  165. $data = array_merge($data, $extend);
  166. Db::startTrans();
  167. try {
  168. $this->model = User::create($data);
  169. $this->token = Random::uuid();
  170. Token::set($this->token, 'user', $this->model->id, $this->keeptime);
  171. Event::trigger('userRegisterSuccessed', $this->model);
  172. Db::commit();
  173. } catch (PDOException|Exception $e) {
  174. $this->setError($e->getMessage());
  175. Db::rollback();
  176. return false;
  177. }
  178. return true;
  179. }
  180. /**
  181. * 会员登录
  182. * @param string $username
  183. * @param string $password
  184. * @param bool $keeptime
  185. * @return bool
  186. * @throws DataNotFoundException
  187. * @throws DbException
  188. * @throws ModelNotFoundException
  189. */
  190. public function login(string $username, string $password, bool $keeptime): bool
  191. {
  192. // 判断账户类型
  193. $accountType = false;
  194. $validate = Validate::rule([
  195. 'mobile' => 'mobile',
  196. 'email' => 'email',
  197. 'username' => 'regex:^[a-zA-Z][a-zA-Z0-9_]{2,15}$',
  198. ]);
  199. if ($validate->check(['mobile' => $username])) $accountType = 'mobile';
  200. if ($validate->check(['email' => $username])) $accountType = 'email';
  201. if ($validate->check(['username' => $username])) $accountType = 'username';
  202. if (!$accountType) {
  203. $this->setError('Account not exist');
  204. return false;
  205. }
  206. $this->model = User::where($accountType, $username)->find();
  207. if (!$this->model) {
  208. $this->setError('Account not exist');
  209. return false;
  210. }
  211. if ($this->model['status'] == 'disable') {
  212. $this->setError('Account disabled');
  213. return false;
  214. }
  215. $userLoginRetry = Config::get('buildadmin.user_login_retry');
  216. if ($userLoginRetry && $this->model->loginfailure >= $userLoginRetry && time() - $this->model->lastlogintime < 86400) {
  217. $this->setError('Please try again after 1 day');
  218. return false;
  219. }
  220. if ($this->model->password != encrypt_password($password, $this->model->salt)) {
  221. $this->loginFailed();
  222. $this->setError('Password is incorrect');
  223. return false;
  224. }
  225. if (Config::get('buildadmin.user_sso')) {
  226. Token::clear('user', $this->model->id);
  227. Token::clear('user-refresh', $this->model->id);
  228. }
  229. if ($keeptime) {
  230. $this->setRefreshToken(2592000);
  231. }
  232. $this->loginSuccessful();
  233. return true;
  234. }
  235. /**
  236. * 直接登录会员账号
  237. * @param int $userId 用户ID
  238. * @return bool
  239. * @throws DataNotFoundException
  240. * @throws DbException
  241. * @throws ModelNotFoundException
  242. */
  243. public function direct(int $userId): bool
  244. {
  245. $this->model = User::find($userId);
  246. if (!$this->model) return false;
  247. if (Config::get('buildadmin.user_sso')) {
  248. Token::clear('user', $this->model->id);
  249. Token::clear('user-refresh', $this->model->id);
  250. }
  251. return $this->loginSuccessful();
  252. }
  253. /**
  254. * 检查旧密码是否正确
  255. * @param $password
  256. * @return bool
  257. */
  258. public function checkPassword($password): bool
  259. {
  260. if ($this->model->password != encrypt_password($password, $this->model->salt)) {
  261. return false;
  262. } else {
  263. return true;
  264. }
  265. }
  266. /**
  267. * 登录成功
  268. * @return bool
  269. */
  270. public function loginSuccessful(): bool
  271. {
  272. if (!$this->model) {
  273. return false;
  274. }
  275. Db::startTrans();
  276. try {
  277. $this->model->loginfailure = 0;
  278. $this->model->lastlogintime = time();
  279. $this->model->lastloginip = request()->ip();
  280. $this->model->save();
  281. $this->logined = true;
  282. if (!$this->token) {
  283. $this->token = Random::uuid();
  284. Token::set($this->token, 'user', $this->model->id, $this->keeptime);
  285. }
  286. Db::commit();
  287. } catch (PDOException|Exception $e) {
  288. Db::rollback();
  289. $this->setError($e->getMessage());
  290. return false;
  291. }
  292. return true;
  293. }
  294. /**
  295. * 登录失败
  296. * @return bool
  297. */
  298. public function loginFailed(): bool
  299. {
  300. if (!$this->model) {
  301. return false;
  302. }
  303. Db::startTrans();
  304. try {
  305. $this->model->loginfailure++;
  306. $this->model->lastlogintime = time();
  307. $this->model->lastloginip = request()->ip();
  308. $this->model->save();
  309. $this->token = '';
  310. $this->model = null;
  311. $this->logined = false;
  312. Db::commit();
  313. } catch (PDOException|Exception $e) {
  314. Db::rollback();
  315. $this->setError($e->getMessage());
  316. return false;
  317. }
  318. return true;
  319. }
  320. /**
  321. * 退出登录
  322. * @return bool
  323. */
  324. public function logout(): bool
  325. {
  326. if (!$this->logined) {
  327. $this->setError('You are not logged in');
  328. return false;
  329. }
  330. $this->logined = false;
  331. Token::delete($this->token);
  332. $this->token = '';
  333. return true;
  334. }
  335. /**
  336. * 是否登录
  337. * @return bool
  338. */
  339. public function isLogin(): bool
  340. {
  341. return $this->logined;
  342. }
  343. /**
  344. * 获取会员模型
  345. * @return User
  346. */
  347. public function getUser(): User
  348. {
  349. return $this->model;
  350. }
  351. /**
  352. * 获取会员Token
  353. * @return string
  354. */
  355. public function getToken(): string
  356. {
  357. return $this->token;
  358. }
  359. /**
  360. * 设置刷新Token
  361. * @param int $keeptime
  362. */
  363. public function setRefreshToken(int $keeptime = 0)
  364. {
  365. $this->refreshToken = Random::uuid();
  366. Token::set($this->refreshToken, 'user-refresh', $this->model->id, $keeptime);
  367. }
  368. /**
  369. * 获取会员刷新Token
  370. * @return string
  371. */
  372. public function getRefreshToken(): string
  373. {
  374. return $this->refreshToken;
  375. }
  376. /**
  377. * 获取会员信息 - 只输出允许输出的字段
  378. * @return array
  379. */
  380. public function getUserInfo(): array
  381. {
  382. if (!$this->model) {
  383. return [];
  384. }
  385. $info = $this->model->toArray();
  386. $info = array_intersect_key($info, array_flip($this->getAllowFields())); // 只输出允许输出的字段,允许输出的字段在模型中定义,
  387. $info['token'] = $this->getToken();
  388. $info['refreshToken'] = $this->getRefreshToken();
  389. return $info;
  390. }
  391. /**
  392. * 获取允许输出字段
  393. * @return string[]
  394. */
  395. public function getAllowFields(): array
  396. {
  397. return $this->allowFields;
  398. }
  399. /**
  400. * 设置允许输出字段
  401. * @param $fields
  402. */
  403. public function setAllowFields($fields)
  404. {
  405. $this->allowFields = $fields;
  406. }
  407. /**
  408. * 设置Token有效期
  409. * @param int $keeptime
  410. */
  411. public function setKeeptime(int $keeptime = 0)
  412. {
  413. $this->keeptime = $keeptime;
  414. }
  415. public function check(string $name, int $uid = 0, string $relation = 'or', string $mode = 'url'): bool
  416. {
  417. return parent::check($name, $uid ?: $this->id, $relation, $mode);
  418. }
  419. public function getRuleList(int $uid = 0): array
  420. {
  421. return parent::getRuleList($uid ?: $this->id);
  422. }
  423. public function getRuleIds(int $uid = 0): array
  424. {
  425. return parent::getRuleIds($uid ?: $this->id);
  426. }
  427. public function getMenus(int $uid = 0): array
  428. {
  429. return parent::getMenus($uid ?: $this->id);
  430. }
  431. public function isSuperUser(): bool
  432. {
  433. return in_array('*', $this->getRuleIds());
  434. }
  435. /**
  436. * 设置错误消息
  437. * @param $error
  438. * @return $this
  439. */
  440. public function setError($error): Auth
  441. {
  442. $this->error = $error;
  443. return $this;
  444. }
  445. /**
  446. * 获取错误消息
  447. * @return float|int|string
  448. */
  449. public function getError()
  450. {
  451. return $this->error ? __($this->error) : '';
  452. }
  453. }