Symfony5.4下AWS S3+Flysystem+LiipImagine预签名URL及403权限问题
问题:Symfony 5.4 + AWS S3 + Flysystem + LiipImagine 私有桶缩略图权限与预签名URL失效问题
环境与配置
IAM权限配置
"Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetObject", "s3:DeleteObject", "s3:GetObjectAcl", "s3:PutObjectAcl", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::myBucket", "arn:aws:s3:::mybucket/*" ]
Symfony配置文件
service.yaml
... parameters: uploads_base_url: '%env(AWS_S3_UPLOAD_BASE_URL)%' services : Aws\S3\S3Client: arguments: - version: '2006-03-01' region: '%env(AWS_S3_ACCESS_REGION)%' credentials: key: '%env(AWS_S3_ACCESS_ID)%' secret: '%env(AWS_S3_ACCESS_SECRET)%'
oneup_flysystem.yaml
... adapters: aws_s3_adapter: awss3v3: client : Aws\S3\S3Client bucket: '%env(AWS_S3_BUCKET_NAME)%' options: ACL: bucket-owner-full-control filesystems: aws_s3_system: adapter: aws_s3_adapter
liip_imagine.yaml
... driver: "gd" loaders: aws_s3_loader: flysystem: filesystem_service: oneup_flysystem.aws_s3_system_filesystem data_loader: aws_s3_loader resolvers: aws_s3_resolver: flysystem: filesystem_service: oneup_flysystem.aws_s3_system_filesystem root_url: '%uploads_base_url%' cache_prefix: media/cache cache: aws_s3_resolver filter_sets: squared_thumbnail_small: quality: 70 filters: thumbnail: size: [50, 50] mode: outbound
Twig与服务代码
Twig模板调用
<a href="{{ assetPresigned('photos', player.photoFilename) }}" target="_blank"> <img src="{{ assetPresigned('photos', player.photoFilename ) | imagine_filter('squared_thumbnail_small') }}" alt="photo"> </a>
FileUploadService.php 原方法
public function assetPresigned(string $folder, string $filename): string { $command = $this->s3Client->getCommand('GetObject', [ 'Bucket' => $this->awsS3BucketName, 'Key' => $folder.'/'.$filename, ]); $urlPresigned = $this->s3Client->createPresignedRequest($command, '+5 minutes'); return ((string) $urlPresigned->getUri()); }
核心问题
imagine_filter会重写预签名URL,移除签名信息,导致img标签无法访问私有桶中的缩略图- 手动触发缩略图生成时,存储到S3返回
403 Forbidden错误,推测权限或凭证配置异常
解决方案
步骤1:修复IAM权限大小写问题
S3的ARN严格区分大小写,将IAM配置中的Resource路径改为与Bucket名完全一致:
"Resource": [ "arn:aws:s3:::myBucket", "arn:aws:s3:::myBucket/*" ]
步骤2:修改服务代码,支持缩略图预签名URL生成
重构FileUploadService,利用LiipImagine的CacheManager自动处理缩略图生成,同时对缩略图路径生成预签名:
namespace App\Service; use Aws\S3\S3Client; use Liip\ImagineBundle\Imagine\Cache\CacheManager; class FileUploadService { private $s3Client; private $awsS3BucketName; private $cacheManager; public function __construct(S3Client $s3Client, string $awsS3BucketName, CacheManager $cacheManager) { $this->s3Client = $s3Client; $this->awsS3BucketName = $awsS3BucketName; $this->cacheManager = $cacheManager; } // 原方法:生成原文件预签名URL public function assetPresigned(string $folder, string $filename): string { $command = $this->s3Client->getCommand('GetObject', [ 'Bucket' => $this->awsS3BucketName, 'Key' => $folder.'/'.$filename, ]); $urlPresigned = $this->s3Client->createPresignedRequest($command, '+5 minutes'); return (string) $urlPresigned->getUri(); } // 新增方法:生成缩略图预签名URL public function thumbnailPresigned(string $folder, string $filename, string $filter): string { $originalPath = $folder.'/'.$filename; // 自动触发缩略图生成与缓存 if (!$this->cacheManager->isStored($originalPath, $filter)) { $this->cacheManager->resolve($originalPath, $filter); } // 获取缩略图在S3中的存储Key $thumbnailBrowserPath = $this->cacheManager->getBrowserPath($originalPath, $filter); $thumbnailKey = ltrim(str_replace($_ENV['AWS_S3_UPLOAD_BASE_URL'], '', $thumbnailBrowserPath), '/'); // 生成缩略图预签名URL $command = $this->s3Client->getCommand('GetObject', [ 'Bucket' => $this->awsS3BucketName, 'Key' => $thumbnailKey, ]); $urlPresigned = $this->s3Client->createPresignedRequest($command, '+5 minutes'); return (string) $urlPresigned->getUri(); } }
步骤3:修改Twig调用逻辑
直接调用新增的thumbnailPresigned方法,避免对预签名URL应用过滤器:
<a href="{{ assetPresigned('photos', player.photoFilename) }}" target="_blank"> <img src="{{ thumbnailPresigned('photos', player.photoFilename, 'squared_thumbnail_small') }}" alt="photo"> </a>
关键说明
- IAM权限的大小写不匹配是403错误的核心原因,必须保证Resource路径与Bucket名完全一致
- 原逻辑中对预签名URL应用
imagine_filter是错误的,过滤器会生成公开缓存URL,正确做法是对缩略图的存储路径单独生成预签名 - 利用LiipImagine的
CacheManager自动处理缩略图生成与存储,无需手动调用底层的DataManager和FilterManager,减少代码复杂度
内容的提问来源于stack exchange,提问作者brico
相关产品推荐
相关产品推荐

