Auth.php 11 KB

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