88c6ce8ccc
- 迁移为 frontend-web、frontend-electron、backend-web 与 docker 部署结构 - 网页端:订阅门禁二次弹窗、套餐/支付组件化、顶栏分组对齐 - 首页:最近文件与模板库布局优化,缩略图对齐,下载与删除操作 - 新增管理后台、支付与云端文件 API,更新 README 与项目规范 Co-authored-by: Cursor <cursoragent@cursor.com>
63 lines
1.9 KiB
PHP
63 lines
1.9 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Soon\Api\Middleware;
|
|
|
|
use Soon\Api\Core\Db;
|
|
use Soon\Api\Core\Json;
|
|
use Soon\Api\Core\Jwt;
|
|
|
|
/**
|
|
* 普通用户 JWT 鉴权。
|
|
*/
|
|
final class Auth
|
|
{
|
|
public static function bearerFromGlobals(): string
|
|
{
|
|
$h = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
|
|
if ($h === '' && function_exists('apache_request_headers')) {
|
|
$headers = apache_request_headers();
|
|
$h = $headers['Authorization'] ?? '';
|
|
}
|
|
if ($h === '' || stripos($h, 'Bearer ') !== 0) {
|
|
return '';
|
|
}
|
|
return trim(substr($h, 7));
|
|
}
|
|
|
|
public static function require(): array
|
|
{
|
|
$token = self::bearerFromGlobals();
|
|
if ($token === '') {
|
|
Json::fail('unauthorized', '缺少访问令牌', 401);
|
|
}
|
|
$payload = Jwt::decode($token);
|
|
if ($payload === null) {
|
|
Json::fail('unauthorized', '令牌无效或已过期', 401);
|
|
}
|
|
if (($payload['typ'] ?? '') !== 'access') {
|
|
Json::fail('unauthorized', '需要访问令牌', 401);
|
|
}
|
|
$uid = (int)($payload['sub'] ?? 0);
|
|
if ($uid <= 0) {
|
|
Json::fail('unauthorized', '令牌主体错误', 401);
|
|
}
|
|
$stmt = Db::pdo()->prepare('SELECT id, email, role, admin_level, status FROM users WHERE id = :id');
|
|
$stmt->execute(['id' => $uid]);
|
|
$user = $stmt->fetch();
|
|
if (!$user || $user['status'] === 'disabled') {
|
|
Json::fail('unauthorized', '账号不存在或已停用', 401);
|
|
}
|
|
$out = [
|
|
'id' => (int)$user['id'],
|
|
'email' => $user['email'],
|
|
'role' => $user['role'],
|
|
];
|
|
if ($user['role'] === 'admin') {
|
|
$level = (string)($user['admin_level'] ?? 'full');
|
|
$out['admin_level'] = $level === 'ops' ? 'ops' : 'full';
|
|
}
|
|
return $out;
|
|
}
|
|
}
|