161 lines
5.7 KiB
PHP
161 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace app\support;
|
|
|
|
use Webman\Http\Request;
|
|
use support\think\Db;
|
|
|
|
class AdminAuthService
|
|
{
|
|
public function __construct()
|
|
{
|
|
$this->ensureTokenTable();
|
|
(new AdminAccessService())->bootstrapDefaults();
|
|
}
|
|
|
|
public function login(string $mobile, string $password, Request $request): array
|
|
{
|
|
$admin = Db::name('admin_users')->where('mobile', $mobile)->find();
|
|
if (!$admin || ($admin['status'] ?? 'enabled') !== 'enabled') {
|
|
throw new \RuntimeException('账号不存在或已停用');
|
|
}
|
|
|
|
if (!password_verify($password, (string)$admin['password'])) {
|
|
throw new \RuntimeException('手机号或密码错误');
|
|
}
|
|
|
|
$token = bin2hex(random_bytes(24));
|
|
$tokenHash = hash('sha256', $token);
|
|
$now = date('Y-m-d H:i:s');
|
|
$expireTime = date('Y-m-d H:i:s', time() + 7 * 24 * 3600);
|
|
|
|
// Allow concurrent logins across devices/browsers. Only clean up this user's expired tokens.
|
|
Db::name('admin_api_tokens')
|
|
->where('admin_user_id', $admin['id'])
|
|
->where('expire_time', '<', $now)
|
|
->delete();
|
|
Db::name('admin_api_tokens')->insert([
|
|
'admin_user_id' => (int)$admin['id'],
|
|
'token_hash' => $tokenHash,
|
|
'expire_time' => $expireTime,
|
|
'last_active_at' => $now,
|
|
'last_ip' => $request->getRealIp(),
|
|
'user_agent' => substr((string)$request->header('user-agent', ''), 0, 500),
|
|
'created_at' => $now,
|
|
'updated_at' => $now,
|
|
]);
|
|
|
|
Db::name('admin_users')->where('id', $admin['id'])->update([
|
|
'last_login_at' => $now,
|
|
'updated_at' => $now,
|
|
]);
|
|
|
|
return [
|
|
'token' => $token,
|
|
'admin_info' => $this->adminInfo((int)$admin['id']),
|
|
];
|
|
}
|
|
|
|
public function logout(Request $request): void
|
|
{
|
|
$token = $this->extractToken($request);
|
|
if ($token === '') {
|
|
return;
|
|
}
|
|
|
|
Db::name('admin_api_tokens')->where('token_hash', hash('sha256', $token))->delete();
|
|
}
|
|
|
|
public function current(Request $request): ?array
|
|
{
|
|
$token = $this->extractToken($request);
|
|
if ($token === '') {
|
|
return null;
|
|
}
|
|
|
|
$record = Db::name('admin_api_tokens')
|
|
->where('token_hash', hash('sha256', $token))
|
|
->find();
|
|
if (!$record) {
|
|
return null;
|
|
}
|
|
|
|
if (!empty($record['expire_time']) && strtotime((string)$record['expire_time']) < time()) {
|
|
Db::name('admin_api_tokens')->where('id', $record['id'])->delete();
|
|
return null;
|
|
}
|
|
|
|
$admin = Db::name('admin_users')->where('id', $record['admin_user_id'])->find();
|
|
if (!$admin || ($admin['status'] ?? 'enabled') !== 'enabled') {
|
|
return null;
|
|
}
|
|
|
|
Db::name('admin_api_tokens')->where('id', $record['id'])->update([
|
|
'last_active_at' => date('Y-m-d H:i:s'),
|
|
'last_ip' => $request->getRealIp(),
|
|
'user_agent' => substr((string)$request->header('user-agent', ''), 0, 500),
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
]);
|
|
|
|
return $this->adminInfo((int)$admin['id']);
|
|
}
|
|
|
|
public function hasPermission(array $adminInfo, string $permissionCode): bool
|
|
{
|
|
if ($permissionCode === '') {
|
|
return true;
|
|
}
|
|
|
|
return in_array($permissionCode, $adminInfo['permission_codes'], true);
|
|
}
|
|
|
|
private function adminInfo(int $adminUserId): array
|
|
{
|
|
$admin = Db::name('admin_users')->where('id', $adminUserId)->find();
|
|
$roleIds = Db::name('admin_role_relations')->where('admin_user_id', $adminUserId)->column('role_id');
|
|
$roles = $roleIds ? Db::name('admin_roles')->whereIn('id', $roleIds)->select()->toArray() : [];
|
|
$permissionIds = $roleIds ? Db::name('admin_role_permissions')->whereIn('role_id', $roleIds)->column('permission_id') : [];
|
|
$permissions = $permissionIds ? Db::name('admin_permissions')->whereIn('id', $permissionIds)->select()->toArray() : [];
|
|
|
|
return [
|
|
'id' => (int)($admin['id'] ?? 0),
|
|
'name' => $admin['name'] ?? '',
|
|
'mobile' => $admin['mobile'] ?? '',
|
|
'email' => $admin['email'] ?? '',
|
|
'status' => $admin['status'] ?? 'enabled',
|
|
'role_names' => array_values(array_map(fn (array $item) => $item['name'], $roles)),
|
|
'permission_codes' => array_values(array_unique(array_map(fn (array $item) => $item['code'], $permissions))),
|
|
];
|
|
}
|
|
|
|
private function extractToken(Request $request): string
|
|
{
|
|
$authorization = trim((string)$request->header('authorization', ''));
|
|
if (preg_match('/^Bearer\s+(.+)$/i', $authorization, $matches)) {
|
|
return trim($matches[1]);
|
|
}
|
|
return '';
|
|
}
|
|
|
|
private function ensureTokenTable(): void
|
|
{
|
|
Db::execute(<<<'SQL'
|
|
CREATE TABLE IF NOT EXISTS admin_api_tokens (
|
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
admin_user_id BIGINT UNSIGNED NOT NULL,
|
|
token_hash VARCHAR(64) NOT NULL,
|
|
expire_time DATETIME NOT NULL,
|
|
last_active_at DATETIME NULL DEFAULT NULL,
|
|
last_ip VARCHAR(64) NOT NULL DEFAULT '',
|
|
user_agent VARCHAR(500) NOT NULL DEFAULT '',
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY uk_admin_api_tokens_token_hash (token_hash),
|
|
KEY idx_admin_api_tokens_admin_user_id (admin_user_id),
|
|
KEY idx_admin_api_tokens_expire_time (expire_time)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='后台登录Token';
|
|
SQL);
|
|
}
|
|
}
|