// +---------------------------------------------------------------------- declare(strict_types=1); namespace ywxapp\library; use think\App; use think\exception\HttpException; use think\facade\Db; use think\facade\Event; use think\facade\Validate; use ywxapp\library\Result; use ywxapp\model\BackendAdmin as AdminModel; use ywxapp\service\JwtService; use ywxapp\utils\Random; /** * 后台(管理员)鉴权类,继承前台 Auth,仅重写差异钩子与业务逻辑。 * * 差异点: * - 模型:Backend 模型(主键 id) * - 角色字段:roles;权限集:getPermissionNames() * - 登录事件:user_login_successed * - 登出:不清整个 session(仅清 token),保持与原 Backend 行为一致 * - 登录/注册/改密:后台业务逻辑(查 Backend 表) */ class AdminAuth extends Auth { /** * 超级管理员 */ protected $isSuperAdmin = false; /** * 超级管理员 */ private $superAdmin = 1; /** * 允许查询的字段(后台) * @var array */ protected $allowFields = ['id', 'account', 'nickname', 'roles', 'avatar', 'score']; /** * 构造方法 * @param App $app */ public function __construct(App $app, $options = []) { parent::__construct($app, $options); $this->isAdmin = true; // 标记后台管理员上下文,供 tryLoginByToken 做 isAdmin 一致性校验 } /** * 返回管理员模型类(主键为 id) */ protected function getUserModel(): string { return AdminModel::class; } /** * 后台角色取自 roles 字段(前台 Auth 默认取 groups,此处重写) */ protected function resolveRoles($info) { return $info->roles ?? []; } /** * 管理员权限取自模型方法(后台式权限集) */ protected function resolvePowers($info): array { return $info->getPermissionNames(); } /** * 管理员不存在时的错误提示 */ protected function notFoundMessage($id): string { return "AdminId:$id is incorrect"; } /** * 管理员登录成功后触发事件 */ protected function afterLogin($info): void { Event::trigger('user_login_successed', $this->info); } /** * 后台登出:仅清 token(不清整个 session),保持与原 Backend 行为一致 */ protected function afterLogout(): void {} /** * 添加管理员(后台). * * @param string $username 用户名 * @param string $password 密码 * @param array $extend 扩展参数 * @return void */ public function register($account = '', $password = '', $email = '', $mobile = '', $extend = []) { AdminModel::ensureSchema(); // 后台核心表自愈(缺表/缺列兜底) if (AdminModel::getByAccount($account)) { Result::instance()->error('Account already exist'); } $data = [ 'password' => password_hash($password ? $password : Random::alpha(6), PASSWORD_DEFAULT, ['cost' => 12]), 'status' => 0, 't1' => $account, ]; $field = Validate::is($account, 'email') ? 'email' : (Validate::is($account, 'mobile') ? 'mobile' : 'account'); $data[$field] = $account; $data = array_merge($data, $extend); $params = Event::trigger('AdminBeforeRegister', $data, true); $data = array_merge($data, $params); Db::startTrans(); try { $info = AdminModel::create($data); $this->info = AdminModel::find($info->id); Event::trigger('AdminAfterRegister', $this->info); Db::commit(); $newClaims = [ 'uid' => $info->id, 'isAdmin' => true, 'account' => $info->account, ]; $tokens = JwtService::instance()->createToken($newClaims); $this->persistTokens($tokens); } catch (\think\Exception $e) { Db::rollback(); Result::instance()->error($e->getMessage()); } } /** * 管理员登录(后台). * * @param string $account 账号,用户名、邮箱、手机号 * @param string $password 密码 * @return void */ public function login($account, $password, $isAuthPass = true) { AdminModel::ensureSchema(); // 后台核心表自愈(缺表/缺列兜底) $field = Validate::is($account, 'email') ? 'email' : (Validate::regex( $account, '/^1\d{10}$/' ) ? 'mobile' : 'account'); $info = AdminModel::where([$field => $account])->find(); if (! $info) { Result::instance()->error('Account is incorrect'); } if ($info->status != 1) { Result::instance()->error('Account is locked'); } $info->resetPassword($password); // 验证密码(直接使用库中存储的哈希,禁止先 resetPassword) if (! $info->checkPassword($password)) { $info->recordLoginFail($this->app->request->ip()); // 记录失败 Result::instance()->error('密码错误', 4011); } $info->recordLoginSuccess(); $newClaims = [ 'uid' => $info->id, 'isAdmin' => true, 'role' => 'admin', 'account' => $info->account, ]; $tokens = JwtService::instance()->createToken($newClaims); $this->persistTokens($tokens); $this->initUser($info->id); } /** * 修改密码(后台) * @param string $newpassword 新密码 * @param string $oldpassword 旧密码 * @param bool $ignoreoldpassword 忽略旧密码 * @return boolean */ public function changepwd($newpassword, $oldpassword = '', $ignoreoldpassword = false) { if (! $this->_logined) { $this->setError('You are not logged in'); return false; } //判断旧密码是否正确 if ($this->_user->password == $this->getEncryptPassword($oldpassword, $this->_user->salt) || $ignoreoldpassword) { Db::startTrans(); try { $salt = Random::alnum(); $newpassword = $this->getEncryptPassword($newpassword, $salt); $this->_user->save(['loginfailure' => 0, 'password' => $newpassword, 'salt' => $salt]); Token::clear($this->_user->uid); //修改密码成功的事件 Hook::listen("user_changepwd_successed", $this->_user); Db::commit(); } catch (Exception $e) { Db::rollback(); $this->setError($e->getMessage()); return false; } return true; } else { $this->setError('Password is incorrect'); return false; } } }