ScopeService.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. <?php
  2. namespace App\Module\OpenAPI\Services;
  3. use App\Module\OpenAPI\Models\OpenApiApp;
  4. use App\Module\OpenAPI\Models\OpenApiScope;
  5. use App\Module\OpenAPI\Enums\SCOPE_TYPE;
  6. use Illuminate\Support\Facades\Cache;
  7. use Illuminate\Support\Facades\Log;
  8. /**
  9. * 权限范围服务
  10. *
  11. * 提供API权限范围管理和验证功能
  12. */
  13. class ScopeService
  14. {
  15. /**
  16. * 检查应用是否具有指定权限
  17. *
  18. * @param OpenApiApp $app
  19. * @param string $requiredScope
  20. * @return bool
  21. */
  22. public function hasScope(OpenApiApp $app, string $requiredScope): bool
  23. {
  24. // 获取应用的权限范围
  25. $appScopes = $this->getAppScopes($app);
  26. // 如果应用没有配置权限范围,拒绝访问
  27. if (empty($appScopes)) {
  28. return false;
  29. }
  30. // 检查是否有管理员权限(拥有所有权限)
  31. if (in_array('ADMIN', $appScopes)) {
  32. return true;
  33. }
  34. // 检查是否直接拥有所需权限
  35. if (in_array($requiredScope, $appScopes)) {
  36. return true;
  37. }
  38. // 检查通配符权限
  39. if (in_array('*', $appScopes)) {
  40. return true;
  41. }
  42. // 检查权限依赖关系
  43. return $this->checkScopeDependencies($appScopes, $requiredScope);
  44. }
  45. /**
  46. * 检查多个权限
  47. *
  48. * @param OpenApiApp $app
  49. * @param array $requiredScopes
  50. * @param bool $requireAll 是否需要全部权限,false表示只需要其中一个
  51. * @return bool
  52. */
  53. public function hasScopes(OpenApiApp $app, array $requiredScopes, bool $requireAll = true): bool
  54. {
  55. if ($requireAll) {
  56. // 需要全部权限
  57. foreach ($requiredScopes as $scope) {
  58. if (!$this->hasScope($app, $scope)) {
  59. return false;
  60. }
  61. }
  62. return true;
  63. } else {
  64. // 只需要其中一个权限
  65. foreach ($requiredScopes as $scope) {
  66. if ($this->hasScope($app, $scope)) {
  67. return true;
  68. }
  69. }
  70. return false;
  71. }
  72. }
  73. /**
  74. * 获取应用的权限范围
  75. *
  76. * @param OpenApiApp $app
  77. * @return array
  78. */
  79. public function getAppScopes(OpenApiApp $app): array
  80. {
  81. // 优先从缓存获取
  82. $cacheKey = "app_scopes:{$app->app_id}";
  83. $scopes = Cache::get($cacheKey);
  84. if ($scopes === null) {
  85. $scopes = $app->scopes ?? [];
  86. // 缓存权限信息
  87. Cache::put($cacheKey, $scopes, 3600); // 缓存1小时
  88. }
  89. return $scopes;
  90. }
  91. /**
  92. * 更新应用权限范围
  93. *
  94. * @param OpenApiApp $app
  95. * @param array $scopes
  96. * @return bool
  97. */
  98. public function updateAppScopes(OpenApiApp $app, array $scopes): bool
  99. {
  100. // 验证权限范围
  101. $validationResult = $this->validateScopes($scopes);
  102. if (!$validationResult['valid']) {
  103. Log::warning('Invalid scopes provided', [
  104. 'app_id' => $app->app_id,
  105. 'invalid_scopes' => $validationResult['invalid_scopes'],
  106. ]);
  107. return false;
  108. }
  109. // 更新应用权限
  110. $app->update(['scopes' => $scopes]);
  111. // 清除缓存
  112. $this->clearAppScopeCache($app->app_id);
  113. // 记录权限变更日志
  114. $this->logScopeChange($app, $scopes);
  115. return true;
  116. }
  117. /**
  118. * 验证权限范围
  119. *
  120. * @param array $scopes
  121. * @return array ['valid' => bool, 'invalid_scopes' => array]
  122. */
  123. public function validateScopes(array $scopes): array
  124. {
  125. $validScopes = $this->getAllValidScopes();
  126. $invalidScopes = [];
  127. foreach ($scopes as $scope) {
  128. if (!in_array($scope, $validScopes)) {
  129. $invalidScopes[] = $scope;
  130. }
  131. }
  132. return [
  133. 'valid' => empty($invalidScopes),
  134. 'invalid_scopes' => $invalidScopes,
  135. ];
  136. }
  137. /**
  138. * 获取所有有效的权限范围
  139. *
  140. * @return array
  141. */
  142. public function getAllValidScopes(): array
  143. {
  144. $scopes = array_column(SCOPE_TYPE::cases(), 'value');
  145. // 添加特殊权限
  146. $scopes[] = '*'; // 通配符权限
  147. $scopes[] = 'ADMIN'; // 管理员权限
  148. return $scopes;
  149. }
  150. /**
  151. * 获取权限范围的详细信息
  152. *
  153. * @param string $scope
  154. * @return array|null
  155. */
  156. public function getScopeInfo(string $scope): ?array
  157. {
  158. // 特殊权限处理
  159. if ($scope === '*') {
  160. return [
  161. 'name' => '全部权限',
  162. 'description' => '拥有所有API的访问权限',
  163. 'risk_level' => 'HIGH',
  164. 'category' => 'SPECIAL',
  165. ];
  166. }
  167. if ($scope === 'ADMIN') {
  168. return [
  169. 'name' => '管理员权限',
  170. 'description' => '拥有管理员级别的所有权限',
  171. 'risk_level' => 'CRITICAL',
  172. 'category' => 'SPECIAL',
  173. ];
  174. }
  175. // 从枚举中获取权限信息
  176. foreach (SCOPE_TYPE::cases() as $scopeType) {
  177. if ($scopeType->value === $scope) {
  178. return [
  179. 'name' => $scopeType->getLabel(),
  180. 'description' => $scopeType->getDescription(),
  181. 'risk_level' => $scopeType->getRiskLevel(),
  182. 'category' => $scopeType->getCategory(),
  183. ];
  184. }
  185. }
  186. return null;
  187. }
  188. /**
  189. * 检查权限依赖关系
  190. *
  191. * @param array $appScopes
  192. * @param string $requiredScope
  193. * @return bool
  194. */
  195. protected function checkScopeDependencies(array $appScopes, string $requiredScope): bool
  196. {
  197. // 权限依赖关系映射
  198. $dependencies = [
  199. 'USER_WRITE' => ['USER_READ'],
  200. 'GAME_WRITE' => ['GAME_READ'],
  201. 'ITEM_WRITE' => ['ITEM_READ'],
  202. 'FUND_WRITE' => ['FUND_READ'],
  203. 'TRADE_WRITE' => ['TRADE_READ'],
  204. 'ADMIN_WRITE' => ['ADMIN_READ'],
  205. ];
  206. // 如果需要的权限有依赖,检查是否满足依赖
  207. if (isset($dependencies[$requiredScope])) {
  208. foreach ($dependencies[$requiredScope] as $dependency) {
  209. if (!in_array($dependency, $appScopes)) {
  210. return false;
  211. }
  212. }
  213. }
  214. // 检查是否有更高级的权限包含所需权限
  215. $hierarchies = [
  216. 'USER_READ' => ['USER_WRITE', 'ADMIN'],
  217. 'USER_WRITE' => ['ADMIN'],
  218. 'GAME_READ' => ['GAME_WRITE', 'ADMIN'],
  219. 'GAME_WRITE' => ['ADMIN'],
  220. 'ITEM_READ' => ['ITEM_WRITE', 'ADMIN'],
  221. 'ITEM_WRITE' => ['ADMIN'],
  222. 'FUND_READ' => ['FUND_WRITE', 'ADMIN'],
  223. 'FUND_WRITE' => ['ADMIN'],
  224. 'TRADE_READ' => ['TRADE_WRITE', 'ADMIN'],
  225. 'TRADE_WRITE' => ['ADMIN'],
  226. ];
  227. if (isset($hierarchies[$requiredScope])) {
  228. foreach ($hierarchies[$requiredScope] as $higherScope) {
  229. if (in_array($higherScope, $appScopes)) {
  230. return true;
  231. }
  232. }
  233. }
  234. return false;
  235. }
  236. /**
  237. * 清除应用权限缓存
  238. *
  239. * @param string $appId
  240. * @return void
  241. */
  242. protected function clearAppScopeCache(string $appId): void
  243. {
  244. $cacheKey = "app_scopes:{$appId}";
  245. Cache::forget($cacheKey);
  246. }
  247. /**
  248. * 记录权限变更日志
  249. *
  250. * @param OpenApiApp $app
  251. * @param array $newScopes
  252. * @return void
  253. */
  254. protected function logScopeChange(OpenApiApp $app, array $newScopes): void
  255. {
  256. $oldScopes = $app->getOriginal('scopes') ?? [];
  257. Log::info('App scopes updated', [
  258. 'app_id' => $app->app_id,
  259. 'old_scopes' => $oldScopes,
  260. 'new_scopes' => $newScopes,
  261. 'added_scopes' => array_diff($newScopes, $oldScopes),
  262. 'removed_scopes' => array_diff($oldScopes, $newScopes),
  263. ]);
  264. }
  265. /**
  266. * 获取权限范围的分组信息
  267. *
  268. * @return array
  269. */
  270. public function getScopeGroups(): array
  271. {
  272. $groups = [];
  273. foreach (SCOPE_TYPE::cases() as $scope) {
  274. $category = $scope->getCategory();
  275. if (!isset($groups[$category])) {
  276. $groups[$category] = [
  277. 'name' => $category,
  278. 'scopes' => [],
  279. ];
  280. }
  281. $groups[$category]['scopes'][] = [
  282. 'value' => $scope->value,
  283. 'label' => $scope->getLabel(),
  284. 'description' => $scope->getDescription(),
  285. 'risk_level' => $scope->getRiskLevel(),
  286. ];
  287. }
  288. return $groups;
  289. }
  290. }