OpenApiKey.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. <?php
  2. namespace App\Module\OpenAPI\Models;
  3. use UCore\ModelCore;
  4. use Illuminate\Support\Str;
  5. /**
  6. * OpenAPI密钥模型
  7. *
  8. * 管理API密钥的生成、验证和生命周期
  9. */
  10. class OpenApiKey extends ModelCore
  11. {
  12. /**
  13. * 数据表名
  14. *
  15. * @var string
  16. */
  17. protected $table = 'openapi_keys';
  18. /**
  19. * 可批量赋值的属性
  20. *
  21. * @var array
  22. */
  23. protected $fillable = [
  24. 'app_id',
  25. 'key_id',
  26. 'key_secret',
  27. 'name',
  28. 'description',
  29. 'scopes',
  30. 'status',
  31. 'last_used_at',
  32. 'expires_at',
  33. 'created_at',
  34. 'updated_at',
  35. ];
  36. /**
  37. * 属性类型转换
  38. *
  39. * @var array
  40. */
  41. protected $casts = [
  42. 'scopes' => 'json',
  43. 'last_used_at' => 'datetime',
  44. 'expires_at' => 'datetime',
  45. 'created_at' => 'datetime',
  46. 'updated_at' => 'datetime',
  47. ];
  48. /**
  49. * 隐藏的属性
  50. *
  51. * @var array
  52. */
  53. protected $hidden = [
  54. 'key_secret',
  55. ];
  56. /**
  57. * 状态常量
  58. */
  59. const STATUS_ACTIVE = 'ACTIVE';
  60. const STATUS_INACTIVE = 'INACTIVE';
  61. const STATUS_EXPIRED = 'EXPIRED';
  62. const STATUS_REVOKED = 'REVOKED';
  63. /**
  64. * 关联应用模型
  65. *
  66. * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
  67. */
  68. public function app()
  69. {
  70. return $this->belongsTo(OpenApiApp::class, 'app_id', 'app_id');
  71. }
  72. /**
  73. * 生成新的API密钥
  74. *
  75. * @param string $appId
  76. * @param array $data
  77. * @return static
  78. */
  79. public static function generate(string $appId, array $data = []): self
  80. {
  81. $keyId = 'ak_' . Str::random(32);
  82. $keySecret = 'sk_' . Str::random(64);
  83. return self::create([
  84. 'app_id' => $appId,
  85. 'key_id' => $keyId,
  86. 'key_secret' => hash('sha256', $keySecret), // 加密存储
  87. 'name' => $data['name'] ?? 'Default Key',
  88. 'description' => $data['description'] ?? '',
  89. 'scopes' => $data['scopes'] ?? [],
  90. 'status' => self::STATUS_ACTIVE,
  91. 'expires_at' => $data['expires_at'] ?? null,
  92. ]);
  93. }
  94. /**
  95. * 验证密钥
  96. *
  97. * @param string $keyId
  98. * @param string $keySecret
  99. * @return static|null
  100. */
  101. public static function validateKey(string $keyId, string $keySecret): ?self
  102. {
  103. $key = self::where('key_id', $keyId)
  104. ->where('status', self::STATUS_ACTIVE)
  105. ->first();
  106. if (!$key) {
  107. return null;
  108. }
  109. // 验证密钥
  110. if (!hash_equals($key->key_secret, hash('sha256', $keySecret))) {
  111. return null;
  112. }
  113. // 检查是否过期
  114. if ($key->expires_at && $key->expires_at->isPast()) {
  115. $key->update(['status' => self::STATUS_EXPIRED]);
  116. return null;
  117. }
  118. // 更新最后使用时间
  119. $key->update(['last_used_at' => now()]);
  120. return $key;
  121. }
  122. /**
  123. * 撤销密钥
  124. *
  125. * @return bool
  126. */
  127. public function revoke(): bool
  128. {
  129. return $this->update(['status' => self::STATUS_REVOKED]);
  130. }
  131. /**
  132. * 激活密钥
  133. *
  134. * @return bool
  135. */
  136. public function activate(): bool
  137. {
  138. return $this->update(['status' => self::STATUS_ACTIVE]);
  139. }
  140. /**
  141. * 停用密钥
  142. *
  143. * @return bool
  144. */
  145. public function deactivate(): bool
  146. {
  147. return $this->update(['status' => self::STATUS_INACTIVE]);
  148. }
  149. /**
  150. * 检查密钥是否有效
  151. *
  152. * @return bool
  153. */
  154. public function isValid(): bool
  155. {
  156. if ($this->status !== self::STATUS_ACTIVE) {
  157. return false;
  158. }
  159. if ($this->expires_at && $this->expires_at->isPast()) {
  160. return false;
  161. }
  162. return true;
  163. }
  164. /**
  165. * 检查是否有指定权限
  166. *
  167. * @param string $scope
  168. * @return bool
  169. */
  170. public function hasScope(string $scope): bool
  171. {
  172. $scopes = $this->scopes ?? [];
  173. // 检查是否有管理员权限
  174. if (in_array('ADMIN', $scopes)) {
  175. return true;
  176. }
  177. return in_array($scope, $scopes);
  178. }
  179. /**
  180. * 获取状态标签
  181. *
  182. * @return string
  183. */
  184. public function getStatusLabelAttribute(): string
  185. {
  186. return match($this->status) {
  187. self::STATUS_ACTIVE => '激活',
  188. self::STATUS_INACTIVE => '停用',
  189. self::STATUS_EXPIRED => '已过期',
  190. self::STATUS_REVOKED => '已撤销',
  191. default => '未知',
  192. };
  193. }
  194. /**
  195. * 获取状态颜色
  196. *
  197. * @return string
  198. */
  199. public function getStatusColorAttribute(): string
  200. {
  201. return match($this->status) {
  202. self::STATUS_ACTIVE => 'success',
  203. self::STATUS_INACTIVE => 'warning',
  204. self::STATUS_EXPIRED => 'danger',
  205. self::STATUS_REVOKED => 'secondary',
  206. default => 'secondary',
  207. };
  208. }
  209. /**
  210. * 按应用ID查询
  211. *
  212. * @param \Illuminate\Database\Eloquent\Builder $query
  213. * @param string $appId
  214. * @return \Illuminate\Database\Eloquent\Builder
  215. */
  216. public function scopeByApp($query, string $appId)
  217. {
  218. return $query->where('app_id', $appId);
  219. }
  220. /**
  221. * 只查询有效的密钥
  222. *
  223. * @param \Illuminate\Database\Eloquent\Builder $query
  224. * @return \Illuminate\Database\Eloquent\Builder
  225. */
  226. public function scopeValid($query)
  227. {
  228. return $query->where('status', self::STATUS_ACTIVE)
  229. ->where(function($q) {
  230. $q->whereNull('expires_at')
  231. ->orWhere('expires_at', '>', now());
  232. });
  233. }
  234. /**
  235. * 只查询过期的密钥
  236. *
  237. * @param \Illuminate\Database\Eloquent\Builder $query
  238. * @return \Illuminate\Database\Eloquent\Builder
  239. */
  240. public function scopeExpired($query)
  241. {
  242. return $query->where('expires_at', '<', now());
  243. }
  244. }