Auth.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. <?php
  2. namespace app\admin\library;
  3. use ba\Random;
  4. use think\Exception;
  5. use think\facade\Db;
  6. use think\facade\Config;
  7. use app\admin\model\Admin;
  8. use app\common\facade\Token;
  9. use app\admin\model\AdminGroup;
  10. use think\db\exception\DbException;
  11. use think\db\exception\PDOException;
  12. use think\db\exception\DataNotFoundException;
  13. use think\db\exception\ModelNotFoundException;
  14. use think\facade\Validate;
  15. use think\facade\Event;
  16. /**
  17. * 管理员权限类
  18. */
  19. class Auth extends \ba\Auth
  20. {
  21. /**
  22. * @var Auth 对象实例
  23. */
  24. protected static $instance;
  25. /**
  26. * @var bool 是否登录
  27. */
  28. protected $logined = false;
  29. /**
  30. * @var string 错误消息
  31. */
  32. protected $error = '';
  33. /**
  34. * @var Admin Model实例
  35. */
  36. protected $model = null;
  37. /**
  38. * @var string 令牌
  39. */
  40. protected $token = '';
  41. /**
  42. * @var string 刷新令牌
  43. */
  44. protected $refreshToken = '';
  45. /**
  46. * @var int 令牌默认有效期
  47. */
  48. protected $keeptime = 86400;
  49. /**
  50. * @var string[] 允许输出的字段
  51. */
  52. protected $allowFields = ['id', 'username', 'nickname', 'avatar', "mobile",'email','department_id','jobs_id','lastlogintime'];
  53. public function __construct(array $config = [])
  54. {
  55. parent::__construct($config);
  56. }
  57. /**
  58. * 魔术方法-管理员信息字段
  59. * @param $name
  60. * @return null|string 字段信息
  61. */
  62. public function __get($name)
  63. {
  64. return $this->model ? $this->model->$name : null;
  65. }
  66. /**
  67. * 初始化
  68. * @access public
  69. * @param array $options 参数
  70. * @return Auth
  71. */
  72. public static function instance(array $options = []): Auth
  73. {
  74. if (is_null(self::$instance)) {
  75. self::$instance = new static($options);
  76. }
  77. return self::$instance;
  78. }
  79. /**
  80. * 根据Token初始化管理员登录态
  81. * @param $token
  82. * @return bool
  83. * @throws DataNotFoundException
  84. * @throws DbException
  85. * @throws ModelNotFoundException
  86. */
  87. public function init($token): bool
  88. {
  89. if ($this->logined) {
  90. return true;
  91. }
  92. if ($this->error) {
  93. return false;
  94. }
  95. $tokenData = Token::get($token);
  96. if (!$tokenData) {
  97. return false;
  98. }
  99. $userId = intval($tokenData['user_id']);
  100. if ($userId > 0) {
  101. $this->model = Admin::where('id', $userId)->find();
  102. if (!$this->model) {
  103. $this->setError('Account not exist');
  104. return false;
  105. }
  106. if ($this->model['status'] != '1') {
  107. $this->setError('Account disabled');
  108. return false;
  109. }
  110. $this->token = $token;
  111. $this->loginSuccessful();
  112. return true;
  113. } else {
  114. $this->setError('Token login failed');
  115. return false;
  116. }
  117. }
  118. /**
  119. * 管理员登录
  120. * @param string $username
  121. * @param string $password
  122. * @param bool $keeptime
  123. * @return bool
  124. * @throws DataNotFoundException
  125. * @throws DbException
  126. * @throws ModelNotFoundException
  127. */
  128. public function login(string $username, string $password, bool $keeptime = false): bool
  129. {
  130. $this->model = Admin::where('username', $username)->find();
  131. if (!$this->model) {
  132. $this->setError('Username is incorrect');
  133. return false;
  134. }
  135. if ($this->model['status'] == '0') {
  136. $this->setError('Account disabled');
  137. return false;
  138. }
  139. $adminLoginRetry = Config::get('buildadmin.admin_login_retry');
  140. if ($adminLoginRetry && $this->model->loginfailure >= $adminLoginRetry && time() - $this->model->getData('lastlogintime') < 86400) {
  141. $this->setError('Please try again after 1 day');
  142. return false;
  143. }
  144. if ($this->model->password != encrypt_password($password, $this->model->salt)) {
  145. $this->loginFailed();
  146. $this->setError('Password is incorrect');
  147. return false;
  148. }
  149. if (Config::get('buildadmin.admin_sso')) {
  150. Token::clear('admin', $this->model->id);
  151. Token::clear('admin-refresh', $this->model->id);
  152. }
  153. if ($keeptime) {
  154. $this->setRefreshToken(2592000);
  155. }
  156. $this->loginSuccessful();
  157. return true;
  158. }
  159. /**
  160. * 设置刷新Token
  161. * @param int $keeptime
  162. */
  163. public function setRefreshToken(int $keeptime = 0)
  164. {
  165. $this->refreshToken = Random::uuid();
  166. Token::set($this->refreshToken, 'admin-refresh', $this->model->id, $keeptime);
  167. }
  168. /**
  169. * 管理员登录成功
  170. * @return bool
  171. */
  172. public function loginSuccessful(): bool
  173. {
  174. if (!$this->model) {
  175. return false;
  176. }
  177. Db::startTrans();
  178. try {
  179. $this->model->loginfailure = 0;
  180. $this->model->lastlogintime = time();
  181. $this->model->lastloginip = request()->ip();
  182. $this->model->save();
  183. $this->logined = true;
  184. if (!$this->token) {
  185. $this->token = Random::uuid();
  186. Token::set($this->token, 'admin', $this->model->id, $this->keeptime);
  187. }
  188. Db::commit();
  189. } catch (PDOException|Exception $e) {
  190. Db::rollback();
  191. $this->setError($e->getMessage());
  192. return false;
  193. }
  194. return true;
  195. }
  196. /**
  197. * 管理员登录失败
  198. * @return bool
  199. */
  200. public function loginFailed(): bool
  201. {
  202. if (!$this->model) {
  203. return false;
  204. }
  205. Db::startTrans();
  206. try {
  207. $this->model->loginfailure++;
  208. $this->model->lastlogintime = time();
  209. $this->model->lastloginip = request()->ip();
  210. $this->model->save();
  211. $this->token = '';
  212. $this->model = null;
  213. $this->logined = false;
  214. Db::commit();
  215. } catch (PDOException|Exception $e) {
  216. Db::rollback();
  217. $this->setError($e->getMessage());
  218. return false;
  219. }
  220. return true;
  221. }
  222. /**
  223. * 退出登录
  224. * @return bool
  225. */
  226. public function logout(): bool
  227. {
  228. if (!$this->logined) {
  229. $this->setError('You are not logged in');
  230. return false;
  231. }
  232. $this->logined = false;
  233. Token::delete($this->token);
  234. $this->token = '';
  235. return true;
  236. }
  237. /**
  238. * 是否登录
  239. * @return bool
  240. */
  241. public function isLogin(): bool
  242. {
  243. return $this->logined;
  244. }
  245. /**
  246. * 获取管理员模型
  247. * @return Admin
  248. */
  249. public function getAdmin(): Admin
  250. {
  251. return $this->model;
  252. }
  253. /**
  254. * 获取管理员Token
  255. * @return string
  256. */
  257. public function getToken(): string
  258. {
  259. return $this->token;
  260. }
  261. /**
  262. * 获取管理员刷新Token
  263. * @return string
  264. */
  265. public function getRefreshToken(): string
  266. {
  267. return $this->refreshToken;
  268. }
  269. /**
  270. * 获取管理员信息 - 只输出允许输出的字段
  271. * @return array
  272. */
  273. public function getInfo(): array
  274. {
  275. if (!$this->model) {
  276. return [];
  277. }
  278. $info = $this->model->toArray();
  279. $info = array_intersect_key($info, array_flip($this->getAllowFields()));
  280. $info['token'] = $this->getToken();
  281. $info['refreshToken'] = $this->getRefreshToken();
  282. return $info;
  283. }
  284. /**
  285. * 获取允许输出字段
  286. * @return string[]
  287. */
  288. public function getAllowFields(): array
  289. {
  290. return $this->allowFields;
  291. }
  292. /**
  293. * 设置允许输出字段
  294. * @param $fields
  295. */
  296. public function setAllowFields($fields)
  297. {
  298. $this->allowFields = $fields;
  299. }
  300. /**
  301. * 设置Token有效期
  302. * @param int $keeptime
  303. */
  304. public function setKeeptime(int $keeptime = 0)
  305. {
  306. $this->keeptime = $keeptime;
  307. }
  308. public function check(string $name, int $uid = 0, string $relation = 'or', string $mode = 'url'): bool
  309. {
  310. return parent::check($name, $uid ?: $this->id, $relation, $mode);
  311. }
  312. public function getGroups(int $uid = 0): array
  313. {
  314. return parent::getGroups($uid ?: $this->id);
  315. }
  316. public function getRuleList(int $uid = 0): array
  317. {
  318. return parent::getRuleList($uid ?: $this->id);
  319. }
  320. public function getRuleIds(int $uid = 0): array
  321. {
  322. return parent::getRuleIds($uid ?: $this->id);
  323. }
  324. public function getMenus(int $uid = 0): array
  325. {
  326. return parent::getMenus($uid ?: $this->id);
  327. }
  328. public function isSuperAdmin(): bool
  329. {
  330. return in_array('*', $this->getRuleIds());
  331. }
  332. /**
  333. * 获取管理员所在分组的所有子级分组
  334. * @return array
  335. * @throws DataNotFoundException
  336. * @throws DbException
  337. * @throws ModelNotFoundException
  338. */
  339. public function getAdminChildGroups(): array
  340. {
  341. $groupIds = Db::name('admin_group_access')
  342. ->where('uid', $this->id)
  343. ->select();
  344. $children = [];
  345. foreach ($groupIds as $group) {
  346. $this->getGroupChildGroups($group['group_id'], $children);
  347. }
  348. return array_unique($children);
  349. }
  350. public function getGroupChildGroups($groupId, &$children)
  351. {
  352. $childrenTemp = AdminGroup::where('pid', $groupId)->where('status', '1')->select();
  353. foreach ($childrenTemp as $item) {
  354. $children[] = $item['id'];
  355. $this->getGroupChildGroups($item['id'], $children);
  356. }
  357. }
  358. /**
  359. * 获取分组内的管理员
  360. * @param array $groups
  361. * @return array 管理员数组
  362. */
  363. public function getGroupAdmins(array $groups): array
  364. {
  365. return Db::name('admin_group_access')
  366. ->where('group_id', 'in', $groups)
  367. ->column('uid');
  368. }
  369. /**
  370. * 获取拥有"所有权限"的分组
  371. * @param string $dataLimit 数据权限
  372. * @return array 分组数组
  373. * @throws DataNotFoundException
  374. * @throws DbException
  375. * @throws ModelNotFoundException
  376. */
  377. public function getAllAuthGroups(string $dataLimit): array
  378. {
  379. // 当前管理员拥有的权限
  380. $rules = $this->getRuleIds();
  381. $allAuthGroups = [];
  382. $groups = AdminGroup::where('status', '1')->select();
  383. foreach ($groups as $group) {
  384. if ($group['rules'] == '*') {
  385. continue;
  386. }
  387. $groupRules = explode(',', $group['rules']);
  388. // 及时break, array_diff 等没有 in_array 快
  389. $all = true;
  390. foreach ($groupRules as $groupRule) {
  391. if (!in_array($groupRule, $rules)) {
  392. $all = false;
  393. break;
  394. }
  395. }
  396. if ($all) {
  397. if ($dataLimit == 'allAuth' || ($dataLimit == 'allAuthAndOthers' && array_diff($rules, $groupRules))) {
  398. $allAuthGroups[] = $group['id'];
  399. }
  400. }
  401. }
  402. return $allAuthGroups;
  403. }
  404. /**
  405. * 设置错误消息
  406. * @param $error
  407. * @return $this
  408. */
  409. public function setError($error): Auth
  410. {
  411. $this->error = $error;
  412. return $this;
  413. }
  414. /**
  415. * 获取错误消息
  416. * @return float|int|string
  417. */
  418. public function getError()
  419. {
  420. return $this->error ? __($this->error) : '';
  421. }
  422. /**
  423. * @param string $openid
  424. * @param string $unionid
  425. * @param string $nickname
  426. * @param string $mobile
  427. * @param string $avatar
  428. * @return bool
  429. */
  430. public function WxRegister(string $openid, string $unionid,string $nickname='', string $mobile='',string $avatar=''){
  431. $validate = Validate::rule([
  432. 'openid' => 'require|unique:admin,openid^unionid',
  433. 'unionid' => 'max:255',
  434. ]);
  435. $params = [
  436. 'nickname' => $nickname,
  437. 'openid' => $openid,
  438. 'mobile' => $mobile,
  439. 'unionid' => $unionid,
  440. 'avatar' => $avatar,
  441. ];
  442. if (!$validate->check($params)) {
  443. $this->setError('Registration parameter error');
  444. return false;
  445. }
  446. $ip = request()->ip();
  447. $time = time();
  448. $salt = Random::build('alnum', 16);
  449. $data = [
  450. 'password' => '',
  451. 'joinip' => $ip,
  452. 'jointime' => $time,
  453. 'lastloginip' => $ip,
  454. 'lastlogintime' => $time,
  455. 'salt' => $salt,
  456. 'status' => '1',
  457. ];
  458. $data = array_merge($params, $data);
  459. Db::startTrans();
  460. try {
  461. $this->model = Admin::create($data);
  462. $this->token = Random::uuid();
  463. Token::set($this->token, 'user', $this->model->id, $this->keeptime);
  464. Event::trigger('AdminWxSuccessed', $this->model);
  465. Db::commit();
  466. } catch (PDOException|Exception $e) {
  467. $this->setError($e->getMessage());
  468. Db::rollback();
  469. return false;
  470. }
  471. return true;
  472. }
  473. /** 判断微信账胡是否注册
  474. * @param string $openid
  475. * @param string $unionid
  476. * @return bool
  477. * @throws \think\db\exception\DataNotFoundException
  478. * @throws \think\db\exception\DbException
  479. * @throws \think\db\exception\ModelNotFoundException
  480. */
  481. public function isWxUser(string $openid,string $unionid,bool $keeptime): bool
  482. {
  483. if($openid=='')return false;
  484. $this->model = Admin::where(['openid'=>$openid,"unionid"=>$unionid])->find();
  485. if (!$this->model) {
  486. $this->setError('Account not exist');
  487. return false;
  488. }
  489. if ($this->model['status'] == '0') {
  490. $this->setError('Account disabled');
  491. return false;
  492. }
  493. $adminLoginRetry = Config::get('buildadmin.admin_login_retry');
  494. if ($adminLoginRetry && $this->model->loginfailure >= $adminLoginRetry && time() - $this->model->getData('lastlogintime') < 86400) {
  495. $this->setError('Please try again after 1 day');
  496. return false;
  497. }
  498. // if ($this->model->password != encrypt_password($password, $this->model->salt)) {
  499. // $this->loginFailed();
  500. // $this->setError('Password is incorrect');
  501. // return false;
  502. // }
  503. if (Config::get('buildadmin.admin_sso')) {
  504. Token::clear('admin', $this->model->id);
  505. Token::clear('admin-refresh', $this->model->id);
  506. }
  507. if ($keeptime) {
  508. $this->setRefreshToken(2592000);
  509. }
  510. $this->loginSuccessful();
  511. return true;
  512. }
  513. }