CaiWuXiTong/app/Utilities/QiniuPrivateUrlSigner.php

78 lines
2.5 KiB
PHP

<?php
namespace App\Utilities;
use DateTimeInterface;
use RuntimeException;
class QiniuPrivateUrlSigner
{
public function __construct(private ?array $configuration = null)
{
}
public function supports(string $disk): bool
{
$configuration = $this->configuration ?? config('business_storage', []);
return ($configuration['driver'] ?? null) === 'qiniu'
&& $disk === config('mediable.default_disk');
}
public function forPath(string $path, ?DateTimeInterface $expiration = null): string
{
$configuration = ($this->configuration ?? config('business_storage', []))['qiniu'] ?? [];
$accessKey = (string) ($configuration['access_key'] ?? '');
$secretKey = (string) ($configuration['secret_key'] ?? '');
if ($accessKey === '' || $secretKey === '') {
throw new RuntimeException('业务文件存储凭据不完整。');
}
$deadline = $expiration?->getTimestamp()
?? time() + max(60, (int) ($configuration['signed_url_ttl'] ?? 300));
$urlToSign = $this->unsignedUrlForPath($path) . '?e=' . $deadline;
$signature = strtr(
base64_encode(hash_hmac('sha1', $urlToSign, $secretKey, true)),
'+/',
'-_'
);
return $urlToSign . '&token=' . $accessKey . ':' . $signature;
}
public function unsignedUrlForPath(string $path): string
{
$configuration = ($this->configuration ?? config('business_storage', []))['qiniu'] ?? [];
$domain = rtrim((string) ($configuration['private_domain'] ?? ''), '/');
$root = trim((string) ($configuration['root'] ?? ''), '/');
$objectPath = $this->encodePath(implode('/', array_filter([
$root,
trim(str_replace('\\', '/', $path), '/'),
], static fn ($part) => $part !== '')));
if ($domain === '' || $objectPath === '') {
throw new RuntimeException('业务文件路径无效。');
}
return $domain . '/' . $objectPath;
}
private function encodePath(string $path): string
{
$segments = array_filter(
explode('/', $path),
static fn ($segment) => $segment !== ''
);
foreach ($segments as $segment) {
if ($segment === '.' || $segment === '..' || str_contains($segment, "\0")) {
throw new RuntimeException('业务文件路径无效。');
}
}
return implode('/', array_map('rawurlencode', $segments));
}
}