setKeepTime((int) config('buildadmin.admin_token_keep_time', 86400 * 3)); } public function __get($name): mixed { return $this->model?->$name; } /** * 初始化(Webman:从 request 获取或创建新实例) */ public static function instance(array $options = []): Auth { $request = function_exists('request') ? request() : null; if ($request !== null && isset($request->adminAuth) && $request->adminAuth instanceof Auth) { return $request->adminAuth; } $auth = new static($options); if ($request !== null) { $request->adminAuth = $auth; } return $auth; } public function init(string $token): bool { $tokenData = Token::get($token); if ($tokenData) { Token::tokenExpirationCheck($tokenData); $userId = (int) $tokenData['user_id']; if ($tokenData['type'] === self::TOKEN_TYPE && $userId > 0) { $this->model = Admin::where('id', $userId)->find(); if (!$this->model) { $this->setError('Account not exist'); return false; } if ($this->model['status'] !== 'enable') { $this->setError('Account disabled'); return false; } $this->token = $token; $this->loginSuccessful(); return true; } } $this->setError('Token login failed'); $this->reset(); return false; } public function login(string $username, string $password, bool $keep = false): bool { $this->model = Admin::where('username', $username)->find(); if (!$this->model) { $this->setError('Username is incorrect'); return false; } if ($this->model->status === 'disable') { $this->setError('Account disabled'); return false; } $adminLoginRetry = config('buildadmin.admin_login_retry'); if ($adminLoginRetry) { $lastLoginTime = $this->model->getData('last_login_time'); if ($lastLoginTime) { if ($this->model->login_failure > 0 && time() - $lastLoginTime >= 86400) { $this->model->login_failure = 0; $this->model->save(); $this->model = Admin::where('username', $username)->find(); } if ($this->model->login_failure >= $adminLoginRetry) { $this->setError('Please try again after 1 day'); return false; } } } if (!verify_password($password, $this->model->password, ['salt' => $this->model->salt ?? ''])) { $this->loginFailed(); $this->setError('Password is incorrect'); return false; } if (config('buildadmin.admin_sso')) { Token::clear(self::TOKEN_TYPE, $this->model->id); Token::clear(self::TOKEN_TYPE . '-refresh', $this->model->id); } if ($keep) { $this->setRefreshToken($this->refreshTokenKeepTime); } if (!$this->loginSuccessful()) { return false; } return true; } public function setRefreshToken(int $keepTime = 0): void { $this->refreshToken = Random::uuid(); Token::set($this->refreshToken, self::TOKEN_TYPE . '-refresh', $this->model->id, $keepTime); } public function loginSuccessful(): bool { if (!$this->model) { return false; } $this->model->startTrans(); try { $this->model->login_failure = 0; $this->model->last_login_time = time(); $this->model->last_login_ip = function_exists('request') && request() ? request()->getRealIp() : ''; $this->model->save(); $this->loginEd = true; if (!$this->token) { $this->token = Random::uuid(); Token::set($this->token, self::TOKEN_TYPE, $this->model->id, $this->keepTime); } $this->model->commit(); } catch (Throwable $e) { $this->model->rollback(); $this->setError($e->getMessage()); return false; } return true; } public function loginFailed(): bool { if (!$this->model) { return false; } $this->model->startTrans(); try { $this->model->login_failure++; $this->model->last_login_time = time(); $this->model->last_login_ip = function_exists('request') && request() ? request()->getRealIp() : ''; $this->model->save(); $this->model->commit(); } catch (Throwable $e) { $this->model->rollback(); } return $this->reset(); } public function logout(): bool { if (!$this->loginEd) { $this->setError('You are not logged in'); return false; } return $this->reset(); } public function isLogin(): bool { return $this->loginEd; } public function getAdmin(): Admin { return $this->model; } public function getToken(): string { return $this->token; } public function getRefreshToken(): string { return $this->refreshToken; } public function getInfo(): array { if (!$this->model) { return []; } $info = $this->model->toArray(); $info = array_intersect_key($info, array_flip($this->getAllowFields())); // 与 ThinkPHP 一致:token 为主认证令牌,refresh_token 仅 keep=true 时有值 $token = $this->token; if (!$token && $this->loginEd) { $token = Random::uuid(); Token::set($token, self::TOKEN_TYPE, $this->model->id, $this->keepTime); $this->token = $token; } $info['token'] = $token ?: ''; $info['refresh_token'] = $this->refreshToken ?: ''; // last_login_time 与 ThinkPHP 一致返回整数时间戳 if (isset($info['last_login_time'])) { $info['last_login_time'] = (int) $info['last_login_time']; } return $info; } public function getAllowFields(): array { return $this->allowFields; } public function setAllowFields($fields): void { $this->allowFields = $fields; } public function setKeepTime(int $keepTime = 0): void { $this->keepTime = $keepTime; } public function check(string $name, int $uid = 0, string $relation = 'or', string $mode = 'url'): bool { return parent::check($name, $uid ?: $this->id, $relation, $mode); } public function getGroups(int $uid = 0): array { return parent::getGroups($uid ?: $this->id); } public function getRuleList(int $uid = 0): array { return parent::getRuleList($uid ?: $this->id); } public function getRuleIds(int $uid = 0): array { return parent::getRuleIds($uid ?: $this->id); } public function getMenus(int $uid = 0): array { return parent::getMenus($uid ?: $this->id); } public function isSuperAdmin(): bool { return in_array('*', $this->getRuleIds()); } public function getAdminChildGroups(): array { $groupIds = Db::name('admin_group_access') ->where('uid', $this->id) ->select(); $children = []; foreach ($groupIds as $group) { $this->getGroupChildGroups($group['group_id'], $children); } return array_unique($children); } /** * 本人 + 树形下级角色组内的管理员 ID(与管理员管理列表数据范围一致) */ public function getSelfAndSubordinateAdminIds(): array { if ($this->isSuperAdmin()) { return []; } $descendantGroupIds = $this->getAdminChildGroups(); $adminIds = []; if ($descendantGroupIds !== []) { $adminIds = Db::name('admin_group_access') ->where('group_id', 'in', $descendantGroupIds) ->column('uid'); } $adminIds[] = $this->id; return array_values(array_unique($adminIds)); } public function getGroupChildGroups(int $groupId, array &$children): void { $childrenTemp = AdminGroup::where('pid', $groupId) ->where('status', 1) ->select(); foreach ($childrenTemp as $item) { $children[] = $item['id']; $this->getGroupChildGroups($item['id'], $children); } } public function getGroupAdmins(array $groups): array { return Db::name('admin_group_access') ->where('group_id', 'in', $groups) ->column('uid'); } public function getAllAuthGroups(string $dataLimit, array $groupQueryWhere = [['status', '=', 1]]): array { $rules = $this->getRuleIds(); $allAuthGroups = []; $groups = AdminGroup::where($groupQueryWhere)->select(); foreach ($groups as $group) { if ($group['rules'] === '*') { continue; } $groupRules = explode(',', $group['rules']); $all = true; foreach ($groupRules as $groupRule) { if (!in_array($groupRule, $rules)) { $all = false; break; } } if ($all) { if ($dataLimit === 'allAuth' || ($dataLimit === 'allAuthAndOthers' && array_diff($rules, $groupRules))) { $allAuthGroups[] = $group['id']; } } } return $allAuthGroups; } public function setError($error): Auth { $this->error = $error; return $this; } public function getError(): string { return $this->error ? __($this->error) : ''; } protected function reset(bool $deleteToken = true): bool { if ($deleteToken && $this->token) { Token::delete($this->token); } $this->token = ''; $this->loginEd = false; $this->model = null; $this->refreshToken = ''; $this->setError(''); $this->setKeepTime((int) config('buildadmin.admin_token_keep_time', 86400 * 3)); return true; } }