Files
SoonDesign/backend-web/src/Middleware/Auth.php
T
24kycj 88c6ce8ccc 重构 monorepo 并完善网页端订阅与首页体验
- 迁移为 frontend-web、frontend-electron、backend-web 与 docker 部署结构
- 网页端:订阅门禁二次弹窗、套餐/支付组件化、顶栏分组对齐
- 首页:最近文件与模板库布局优化,缩略图对齐,下载与删除操作
- 新增管理后台、支付与云端文件 API,更新 README 与项目规范

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 18:17:39 +08:00

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;
}
}