Files
YwxAppThink/ywxapp/library/AdminAuth.php
T

227 lines
7.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
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;
}
}
}