AuthValidator.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <?php
  2. namespace App\Module\OpenAPI\Validators;
  3. use UCore\Validator;
  4. use App\Module\OpenAPI\Models\OpenApiApp;
  5. use App\Module\OpenAPI\Enums\AUTH_TYPE;
  6. /**
  7. * 认证验证器
  8. *
  9. * 用于验证认证相关的数据
  10. */
  11. class AuthValidator extends Validator
  12. {
  13. /**
  14. * 验证应用认证信息
  15. *
  16. * @param array $authData
  17. * @param string $arg
  18. * @return void
  19. */
  20. public function validateAppAuth(array $authData, string $arg): void
  21. {
  22. if (!isset($authData['app_id']) || empty($authData['app_id'])) {
  23. $this->addError($arg, '应用ID不能为空');
  24. return;
  25. }
  26. if (!isset($authData['auth_type']) || empty($authData['auth_type'])) {
  27. $this->addError($arg, '认证类型不能为空');
  28. return;
  29. }
  30. // 验证认证类型
  31. $authType = $authData['auth_type'];
  32. $validAuthTypes = array_column(AUTH_TYPE::cases(), 'value');
  33. if (!in_array($authType, $validAuthTypes)) {
  34. $this->addError($arg, '无效的认证类型');
  35. return;
  36. }
  37. // 根据认证类型验证相应的认证信息
  38. switch ($authType) {
  39. case AUTH_TYPE::API_KEY->value:
  40. $this->validateApiKeyAuth($authData, $arg);
  41. break;
  42. case AUTH_TYPE::OAUTH2->value:
  43. $this->validateOAuth2Auth($authData, $arg);
  44. break;
  45. case AUTH_TYPE::JWT->value:
  46. $this->validateJwtAuth($authData, $arg);
  47. break;
  48. case AUTH_TYPE::SIGNATURE->value:
  49. $this->validateSignatureAuth($authData, $arg);
  50. break;
  51. case AUTH_TYPE::BASIC->value:
  52. $this->validateBasicAuth($authData, $arg);
  53. break;
  54. case AUTH_TYPE::BEARER->value:
  55. $this->validateBearerAuth($authData, $arg);
  56. break;
  57. }
  58. }
  59. /**
  60. * 验证API Key认证
  61. *
  62. * @param array $authData
  63. * @param string $arg
  64. * @return void
  65. */
  66. protected function validateApiKeyAuth(array $authData, string $arg): void
  67. {
  68. if (!isset($authData['app_secret']) || empty($authData['app_secret'])) {
  69. $this->addError($arg, 'API密钥不能为空');
  70. return;
  71. }
  72. $appId = $authData['app_id'];
  73. $appSecret = $authData['app_secret'];
  74. // 验证应用ID格式
  75. if (strlen($appId) !== 32 || !ctype_alnum($appId)) {
  76. $this->addError($arg, '应用ID格式错误');
  77. return;
  78. }
  79. // 验证应用密钥格式
  80. if (strlen($appSecret) !== 64 || !ctype_alnum($appSecret)) {
  81. $this->addError($arg, '应用密钥格式错误');
  82. return;
  83. }
  84. // 验证应用是否存在且有效
  85. $app = OpenApiApp::where('app_id', $appId)->first();
  86. if (!$app) {
  87. $this->addError($arg, '应用不存在');
  88. return;
  89. }
  90. if ($app->app_secret !== $appSecret) {
  91. $this->addError($arg, '应用密钥错误');
  92. return;
  93. }
  94. if (!$app->isActive()) {
  95. $this->addError($arg, '应用未激活或已被禁用');
  96. return;
  97. }
  98. }
  99. /**
  100. * 验证OAuth2认证
  101. *
  102. * @param array $authData
  103. * @param string $arg
  104. * @return void
  105. */
  106. protected function validateOAuth2Auth(array $authData, string $arg): void
  107. {
  108. if (!isset($authData['access_token']) || empty($authData['access_token'])) {
  109. $this->addError($arg, '访问令牌不能为空');
  110. return;
  111. }
  112. $accessToken = $authData['access_token'];
  113. // 验证访问令牌格式(这里简化处理,实际应该调用OAuth服务验证)
  114. if (strlen($accessToken) < 32) {
  115. $this->addError($arg, '访问令牌格式错误');
  116. return;
  117. }
  118. // TODO: 调用OAuth模块验证令牌有效性
  119. // $oauthService = app(OAuthService::class);
  120. // if (!$oauthService->validateToken($accessToken)) {
  121. // $this->addError($arg, '访问令牌无效或已过期');
  122. // return;
  123. // }
  124. }
  125. /**
  126. * 验证JWT认证
  127. *
  128. * @param array $authData
  129. * @param string $arg
  130. * @return void
  131. */
  132. protected function validateJwtAuth(array $authData, string $arg): void
  133. {
  134. if (!isset($authData['token']) || empty($authData['token'])) {
  135. $this->addError($arg, 'JWT令牌不能为空');
  136. return;
  137. }
  138. $token = $authData['token'];
  139. // 验证JWT令牌格式
  140. $parts = explode('.', $token);
  141. if (count($parts) !== 3) {
  142. $this->addError($arg, 'JWT令牌格式错误');
  143. return;
  144. }
  145. try {
  146. // 验证JWT令牌(这里简化处理,实际应该使用JWT库验证)
  147. foreach ($parts as $part) {
  148. $decoded = base64_decode($part, true);
  149. if ($decoded === false) {
  150. $this->addError($arg, 'JWT令牌格式错误');
  151. return;
  152. }
  153. }
  154. // TODO: 使用JWT库验证令牌签名和有效期
  155. // $jwt = app(JWTService::class);
  156. // if (!$jwt->validate($token)) {
  157. // $this->addError($arg, 'JWT令牌无效或已过期');
  158. // return;
  159. // }
  160. } catch (\Exception $e) {
  161. $this->addError($arg, 'JWT令牌验证失败');
  162. }
  163. }
  164. /**
  165. * 验证签名认证
  166. *
  167. * @param array $authData
  168. * @param string $arg
  169. * @return void
  170. */
  171. protected function validateSignatureAuth(array $authData, string $arg): void
  172. {
  173. $requiredFields = ['timestamp', 'nonce', 'signature'];
  174. foreach ($requiredFields as $field) {
  175. if (!isset($authData[$field]) || empty($authData[$field])) {
  176. $this->addError($arg, "缺少必需的签名参数: {$field}");
  177. return;
  178. }
  179. }
  180. $timestamp = $authData['timestamp'];
  181. $nonce = $authData['nonce'];
  182. $signature = $authData['signature'];
  183. // 验证时间戳
  184. if (!is_numeric($timestamp)) {
  185. $this->addError($arg, '时间戳格式错误');
  186. return;
  187. }
  188. // 检查时间戳是否在合理范围内(5分钟内)
  189. $currentTime = time();
  190. $timeDiff = abs($currentTime - $timestamp);
  191. if ($timeDiff > 300) {
  192. $this->addError($arg, '请求时间戳过期');
  193. return;
  194. }
  195. // 验证随机数
  196. if (strlen($nonce) < 8) {
  197. $this->addError($arg, '随机数长度不足');
  198. return;
  199. }
  200. // 验证签名格式
  201. if (strlen($signature) !== 64 || !ctype_xdigit($signature)) {
  202. $this->addError($arg, '签名格式错误');
  203. return;
  204. }
  205. // TODO: 验证签名正确性
  206. // $app = OpenApiApp::where('app_id', $authData['app_id'])->first();
  207. // if ($app) {
  208. // $expectedSignature = $this->generateSignature($authData, $app->app_secret);
  209. // if (!hash_equals($expectedSignature, $signature)) {
  210. // $this->addError($arg, '签名验证失败');
  211. // return;
  212. // }
  213. // }
  214. }
  215. /**
  216. * 验证Basic认证
  217. *
  218. * @param array $authData
  219. * @param string $arg
  220. * @return void
  221. */
  222. protected function validateBasicAuth(array $authData, string $arg): void
  223. {
  224. if (!isset($authData['username']) || empty($authData['username'])) {
  225. $this->addError($arg, '用户名不能为空');
  226. return;
  227. }
  228. if (!isset($authData['password']) || empty($authData['password'])) {
  229. $this->addError($arg, '密码不能为空');
  230. return;
  231. }
  232. $username = $authData['username'];
  233. $password = $authData['password'];
  234. // 验证用户名格式(应该是应用ID)
  235. if (strlen($username) !== 32 || !ctype_alnum($username)) {
  236. $this->addError($arg, '用户名格式错误');
  237. return;
  238. }
  239. // 验证密码格式(应该是应用密钥)
  240. if (strlen($password) !== 64 || !ctype_alnum($password)) {
  241. $this->addError($arg, '密码格式错误');
  242. return;
  243. }
  244. // 验证应用信息
  245. $app = OpenApiApp::where('app_id', $username)->first();
  246. if (!$app) {
  247. $this->addError($arg, '应用不存在');
  248. return;
  249. }
  250. if ($app->app_secret !== $password) {
  251. $this->addError($arg, '认证失败');
  252. return;
  253. }
  254. if (!$app->isActive()) {
  255. $this->addError($arg, '应用未激活或已被禁用');
  256. return;
  257. }
  258. }
  259. /**
  260. * 验证Bearer认证
  261. *
  262. * @param array $authData
  263. * @param string $arg
  264. * @return void
  265. */
  266. protected function validateBearerAuth(array $authData, string $arg): void
  267. {
  268. if (!isset($authData['token']) || empty($authData['token'])) {
  269. $this->addError($arg, 'Bearer令牌不能为空');
  270. return;
  271. }
  272. $token = $authData['token'];
  273. // 验证令牌格式
  274. if (strlen($token) < 32) {
  275. $this->addError($arg, 'Bearer令牌格式错误');
  276. return;
  277. }
  278. // TODO: 验证令牌有效性
  279. // 这里可以根据实际需求验证令牌是否有效
  280. // 可能需要查询数据库或调用其他服务
  281. }
  282. /**
  283. * 验证应用状态
  284. *
  285. * @param OpenApiApp $app
  286. * @param string $arg
  287. * @return void
  288. */
  289. public function validateAppStatus(OpenApiApp $app, string $arg): void
  290. {
  291. if (!$app->isActive()) {
  292. $this->addError($arg, '应用未激活或已被禁用');
  293. return;
  294. }
  295. if ($app->isExpired()) {
  296. $this->addError($arg, '应用已过期');
  297. return;
  298. }
  299. if ($app->isSuspended()) {
  300. $this->addError($arg, '应用已被暂停');
  301. return;
  302. }
  303. }
  304. /**
  305. * 验证权限范围
  306. *
  307. * @param OpenApiApp $app
  308. * @param string $requiredScope
  309. * @param string $arg
  310. * @return void
  311. */
  312. public function validateScope(OpenApiApp $app, string $requiredScope, string $arg): void
  313. {
  314. $appScopes = $app->scopes ?? [];
  315. if (empty($appScopes)) {
  316. $this->addError($arg, '应用没有配置权限范围');
  317. return;
  318. }
  319. // 检查是否有管理员权限
  320. if (in_array('ADMIN', $appScopes) || in_array('*', $appScopes)) {
  321. return;
  322. }
  323. // 检查是否有所需权限
  324. if (!in_array($requiredScope, $appScopes)) {
  325. $this->addError($arg, "缺少必需的权限范围: {$requiredScope}");
  326. return;
  327. }
  328. }
  329. }