Laravel使用AWS S3存图后访问报Access Denied如何解决
Laravel 对接 AWS S3 图片访问 AccessDenied 解决方案
核心诱因
- 路径不匹配:上传逻辑将文件存到S3的
images/目录下,但数据库仅保存了纯文件名,调用url()方法时拼接的对象路径缺失images/前缀,实际请求的路径和文件存储路径不一致。 - 权限配置错误:S3存储桶默认私有,若未配置公共读权限或未使用签名链接,直接访问对象地址会被拒绝。
修复步骤
1. 修正路径匹配问题
两种方案选其一即可,优先推荐第一种:
- 方案一:修改上传逻辑,数据库保存完整对象键
将原有上传代码调整为:
调整后原有Blade模板的代码无需修改即可正确拼接路径。$file = $request->featured_image; $filename = time().'.'.$request->featured_image->extension(); $objectKey = 'images/'.$filename; $file->storeAs('images/', $filename, 's3'); $course = Course::create([ 'featured_image' => $objectKey, ]); - 方案二:不修改存量数据,在渲染时手动补全路径
将Blade模板中的图片地址代码修改为:<img src="{{Storage::disk('s3')->url('images/'.$course->featured_image)}}" alt="course">
2. 根据业务场景配置访问权限
场景A:课程封面图为公开可访问资源
这是最常见的业务场景,配置步骤如下:
- 登录AWS S3控制台,找到对应存储桶,关闭「阻止公共访问」相关选项
- 给存储桶添加公开读策略,替换策略内容中的
你的桶名称为实际桶名:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadForImages", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::你的桶名称/*" } ] }
- 修改Laravel项目中
config/filesystems.php的s3磁盘配置,添加上传默认公开权限:
's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), 'url' => env('AWS_URL'), // 新增该行,新上传文件默认设置公开读 'visibility' => 'public', ],
- 之前已经上传的存量文件,需要在S3控制台手动选中文件,将对象ACL设置为公开读。
场景B:图片为私有资源,仅授权用户可访问
这种场景不要开启存储桶公共访问,也不要直接使用url()生成公开链接,改用临时签名链接,示例代码如下:
{{-- 生成有效期为1小时的临时访问链接,过期后链接自动失效 --}} <img src="{{Storage::disk('s3')->temporaryUrl($course->featured_image, now()->addHour())}}" alt="course">
可以根据业务需求自行调整过期时间,该方式不需要修改桶的公共访问配置,安全性更高。
3. 其他排查点
如果以上步骤调整后仍报错,逐一核对以下配置:
- 确认
.env中配置的AWS_BUCKET、AWS_DEFAULT_REGION和实际存储文件的桶信息完全一致,避免连错存储桶 - 确认当前使用的IAM密钥对应的策略没有被其他拒绝策略覆盖,确保拥有
s3:GetObject权限 - 如果存储桶开启了KMS加密,确认访问身份拥有对应KMS密钥的解密权限
内容的提问来源于stack exchange,提问作者Daniel L
相关产品推荐
相关产品推荐

