AWS S3 文件上传返回 403:禁止在 Rails 中使用 Carrierwave/fog gems

tid*_*dal 6 ruby-on-rails amazon-s3 carrierwave railstutorial.org

我已经为此摸不着头脑好几天了。目前正在学习Rails 教程的第 13 章(用户微博),虽然我的应用程序在开发中运行良好,但我似乎无法将图像上传到在生产中运行的 AWS S3(详细信息请参见教程)。该应用程序利用 ASW S3 存储桶进行存储,并利用CarrierWave /Fog gems 进行文件上传。

当我打开我的生产 Heroku 应用程序时,除了图像上传之外,一切正常。当我尝试在新的微帖子上上传图像时,我收到一条通用信息“很抱歉,但出了点问题”。浏览器中的错误。尝试访问存储桶时,Heroku 日志显示 :status 403 错误“Forbidden”(详细日志如下)。

其他人似乎也有类似的问题。在大多数情况下,解决方案似乎是设置适当的 IAM 用户权限设置 S3 存储桶策略,但我相当有信心我已正确设置这些权限,原因有两个:

  1. 我可以使用该用户帐户上的 Web GUI 来上传和管理存储桶中的文件,因此我知道它具有访问权限。
  2. 我安装了 aws 命令行工具,并使用与我的 Web 应用程序中使用的相同的用户访问密钥和密钥对其进行配置,并且我能够通过 CLI 从存储桶上传和检索信息。

尽管如此,我还是尝试了几种存储桶策略和用户权限,包括 Amazon S3 完全访问权限(我相信这是最通用的),以及几个更具体的版本(请参阅下面的最新版本)。

我尝试过的其他似乎对其他人有用的事情:

作为一名新开发人员,我觉得我还没有成熟的技术来诊断这个问题,坦率地说,这有点令人沮丧,所以我希望得到帮助。以下是我的一些问题以及我想进一步研究的可能的调查线索:

  • 问题:如果我能够使用 aws-cli 访问存储桶,我是否也应该能够使用相同的凭据将 heroku 与 Carrierwave/fog 结合使用,或者我是否误解了 heroku 如何访问 S3 存储桶?
  • 可能的问题:上传图像时 Carrierwave 或 ImageMagick 会创建临时文件。在某些情况下,这似乎可能会干扰存储桶上传。有人在这里给出了一个模糊的答案,但我不明白什么行动可以帮助摆脱这个问题。
  • 可能的问题:有人在执行此操作时似乎对 Rails强参数有问题。我不太明白如何调试这个...当我使用 byebug 在服务器中的错误位置(在我的 micropost_controller #create 方法中)打开交互式控制台时,文件上传后,图像位于参数中hash,但:picture实例变量的键@micropost是nil(同时,:content键不是nil,它包含我随图片一起提交的任何文本,因为它应该)。

如果有帮助,请链接到 GitHub 上的其余代码。

抱歉啰嗦了。任何指导将不胜感激。


错误:

2019-01-07T08:11:37.684069+00:00 app[web.1]: F, [2019-01-07T08:11:37.683926 #22] FATAL -- : [363c5115-f872-43b4-857a-10d1d7d11737] Excon::Error::Forbidden (Expected(200) <=> Actual(403 Forbidden)
2019-01-07T08:11:37.684074+00:00 app[web.1]: excon.error.response
2019-01-07T08:11:37.684077+00:00 app[web.1]: :body          => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>***</RequestId><HostId>***=</HostId></Error>"
2019-01-07T08:11:37.684079+00:00 app[web.1]: :cookies       => [
2019-01-07T08:11:37.684081+00:00 app[web.1]: ]
2019-01-07T08:11:37.684082+00:00 app[web.1]: :headers       => {
2019-01-07T08:11:37.684085+00:00 app[web.1]: "Connection"       => "close"
2019-01-07T08:11:37.684086+00:00 app[web.1]: "Content-Type"     => "application/xml"
2019-01-07T08:11:37.684088+00:00 app[web.1]: "Date"             => "Mon, 07 Jan 2019 08:11:36 GMT"
2019-01-07T08:11:37.684090+00:00 app[web.1]: "Server"           => "AmazonS3"
2019-01-07T08:11:37.684092+00:00 app[web.1]: "x-amz-id-2"       => "***"
2019-01-07T08:11:37.684094+00:00 app[web.1]: "x-amz-request-id" => "***"
2019-01-07T08:11:37.684096+00:00 app[web.1]: }
2019-01-07T08:11:37.684098+00:00 app[web.1]: :host          => "bucket-name.s3-us-west-1.amazonaws.com"
2019-01-07T08:11:37.684099+00:00 app[web.1]: :local_address => "*********"
2019-01-07T08:11:37.684101+00:00 app[web.1]: :local_port    => ******
2019-01-07T08:11:37.684103+00:00 app[web.1]: :path          => "/uploads/micropost/picture/306/ocean2.jpeg"
2019-01-07T08:11:37.684104+00:00 app[web.1]: :port          => 443
2019-01-07T08:11:37.684106+00:00 app[web.1]: :reason_phrase => "Forbidden"
2019-01-07T08:11:37.684108+00:00 app[web.1]: :remote_ip     => "*******"
2019-01-07T08:11:37.684110+00:00 app[web.1]: :status        => 403
2019-01-07T08:11:37.684111+00:00 app[web.1]: :status_line   => "HTTP/1.1 403 Forbidden\r\n"
2019-01-07T08:11:37.684113+00:00 app[web.1]: ):
2019-01-07T08:11:37.684217+00:00 app[web.1]: F, [2019-01-07T08:11:37.684147 #22] FATAL -- : [363c5115-f872-43b4-857a-10d1d7d11737]
2019-01-07T08:11:37.684392+00:00 app[web.1]: F, [2019-01-07T08:11:37.684328 #22] FATAL -- : [363c5115-f872-43b4-857a-10d1d7d11737] app/controllers/microposts_controller.rb:7:in `create'
Run Code Online (Sandbox Code Playgroud)

桶政策:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::***********:user/user-name"
            },
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:ListBucket",
                "s3:GetObject",
                "s3:PutObject",
                "s3:PutObjectAcl",
                "s3:DeleteObject",
                "s3:GetObjectVersion"
            ],
            "Resource": [
                "arn:aws:s3:::bucket-name/*",
                "arn:aws:s3:::bucket-name"
            ]
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::*******:user/user-name"
            },
            "Action": [
                "s3:ListBucket",
                "s3:GetBucketLocation"
            ],
            "Resource": "arn:aws:s3:::bucket-name",
            "Condition": {}
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

跨域资源共享:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Run Code Online (Sandbox Code Playgroud)

当前初始化程序:Carrier_wave.rb

if Rails.env.production?
  CarrierWave.configure do |config|
    config.fog_credentials = {
      # Configuration for Amazon S3
      :provider              => 'AWS',
      :aws_access_key_id     => ENV['S3_ACCESS_KEY'],
      :aws_secret_access_key => ENV['S3_SECRET_KEY'],
      :region                => ENV['S3_REGION']
    }
    config.fog_directory     =  ENV['S3_BUCKET']
  end
end
Run Code Online (Sandbox Code Playgroud)

图片上传器.rb

class PictureUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick
  process resize_to_limit: [400, 400]

  if Rails.env.production?
    storage :fog
  else
    storage :file
  end

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  # Add a white list of extensions which are allowed to be uploaded.
  def extension_whitelist
    %w(jpg jpeg gif png)
  end
end
Run Code Online (Sandbox Code Playgroud)

Men*_*usa 0

我看到的所有关于如何使用 Carrierwave/fog 在 S3 中上传文件的教程都在其存储桶上启用了公共访问。由于明显的原因,我没有这样做,因此需要config.aws_acl = :private在我的 Carrierwave.rb 配置文件中使用它。我认为这将帮助那些在遵循在线教程并尝试使其发挥作用后面临相同问题的其他人。