make(JwtAuth::class); return $jwtAuth->createToken($id, $type, $extra + ['auth' => $authHash]); } /** * 解析并校验 access token。 * * @param callable $resolver 根据 token 内的 id 读取账号模型或数组 * @param callable $authHashResolver 根据账号返回当前有效的 auth hash * @param callable|null $accountValidator 根据账号判断当前 token 是否仍可用 * @return mixed * @throws \Psr\SimpleCache\InvalidArgumentException */ public function parseToken(string $token, string $type, callable $resolver, callable $authHashResolver, callable $accountValidator = null) { if (!$token || $token === 'undefined') { throw new AuthException(ApiErrorCode::ERR_LOGIN); } /** @var JwtAuth $jwtAuth */ $jwtAuth = app()->make(JwtAuth::class); [$id, $tokenType, $auth] = $jwtAuth->parseToken($token); if (!$id || $tokenType !== $type) { throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID); } $md5Token = md5($token); $cacheToken = CacheService::redisHandler($type)->get($md5Token, null); if (!$cacheToken) { throw new AuthException(ApiErrorCode::ERR_LOGIN); } if (isset($cacheToken['invalidNum']) && $cacheToken['invalidNum'] >= 3) { $this->clearToken($md5Token, $type); throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID); } try { $jwtAuth->verifyToken(); CacheService::setTokenBucket($md5Token, $cacheToken, $cacheToken['exp'] ?? null, $type); } catch (ExpiredException $e) { $cacheToken['invalidNum'] = ($cacheToken['invalidNum'] ?? 0) + 1; CacheService::setTokenBucket($md5Token, $cacheToken, $cacheToken['exp'] ?? null, $type); throw new AuthException(ApiErrorCode::ERR_LOGIN); } catch (\Throwable $e) { $this->clearToken($md5Token, $type); throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID); } $account = $resolver($id); if (!$account) { $this->clearToken($md5Token, $type); throw new AuthException(ApiErrorCode::ERR_LOGIN); } if ($accountValidator && !$accountValidator($account)) { $this->clearToken($md5Token, $type); throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID); } if ($auth !== $authHashResolver($account)) { throw new AuthException(ApiErrorCode::ERR_LOGIN_INVALID); } return $account; } protected function clearToken(string $md5Token, string $type): void { if (!request()->isCli()) { CacheService::redisHandler($type)->delete($md5Token); } } }