如何随机生成文件链接?

时间:2013-11-07 21:44:58

标签: ruby-on-rails ruby amazon-web-services

我有一个PDF文件,我不想通过我网站上的网址公开访问。作为一个(轻微的)安全层,我希望通过电子邮件向用户发送一个随机生成的独特URL,他们可以从中下载PDF,我将在AWS或类似的东西上存储这些PDF。

我觉得我被困在routes.rb监狱,我不知道如何动态生成URL,也不知道如何正确创建随机URL,跟踪它们或将它们链接到本地​​存储的文件或在AWS上。

有没有人有任何建议来解决这个问题?

3 个答案:

答案 0 :(得分:5)

您如何存储PDF?如果您使用Paperclip之类的内容,则可以非常轻松地生成私有文件的临时公用URL:

class Attachment < ActiveRecord::Base

  has_attached_file :file

  # ...

  def file_url
    file.expiring_url(10)
  end

end

file_url将为该文件生成10秒有效的网址。然后,在任何相关的控制器中,您可以为文件本身设置一个“show”方法,在访问时可以快速重定向到私人URL:

class AttachmentsController < ApplicationController

  # GET /whatever/attachments/:id
  def show
    redirect_to Attachment.find(params[:id]).file_url
  end

end

要实现您要发送的“随机”网址,您需要额外的一步。您可以使用类似SecureRandom.uuid的内容生成长哈希,并将其作为参数传递,并使用AttachmentProxy之类的简单模型。

类似的东西:

class AttachmentProxy < ActiveRecord::Base

  has_one :attachment

  # has an attribute called 'key', indexed...

end

在你的控制器中为这些:

class AttachmentProxyController < ApplicationController

  def show
    proxy = AttachmentProxy.find_by_key(params[:key])
    redirect_to proxy.attachment.file_url
  end

end

答案 1 :(得分:3)

是的,我以前做过这件事。我假设你将使用文件上传宝石,如Paperclip,并创建了一些像Pdf这样的模型:

class Pdf < ActiveRecord::Base
  has_attached_file :pdf, storage: :s3
end

这会设置模型,以便您可以将文件上传到它,并将其存储在AWS S3中。您目前可能没有这种方式,但的想法是拥有一个数据库记录,您可以在其中引用Pdf的URL以及用户将在不知道真实URL的情况下检索它的唯一标记。

在Pdf模型中,您应该有一个令牌:字符串字段,并且在模型中的before_save过滤器中生成唯一令牌:

class Pdf < ActiveRecord::Base
  require 'securerandom'
  has_attached_file :pdf, storage: :s3
  before_save :generate_unique_token

  private

  def generate_unique_token
    self.token ||= SecureRandom.hex
  end
end

现在您可以设置命名路线:

get '/hidden_pdf/:token', to: 'pdfs#get_hidden'

将get_hidden操作添加到Pdfs控制器:

class PdfsController < ApplicationController
  def get_hidden
    pdf = Pdf.where(token: params[:token]).first

    if pdf
      # from the pdf model you have access to its real URL and you can send it directly
      data = open pdf.url
      send_data data.read, filename: pdf.pdf_file_name, type: "application/pdf", disposition: 'inline', stream: 'true', buffer_size: '4096'
    else
      # Not found logic here
    end
  end
end

现在,您只需向用户发送一个网址,例如 myapp.com/pdfs/random-string-here ,当他们转到该网址时,您的应用就会通过该令牌找到数据库中的记录,在AWS上提取PDF的真实URL,从中读取数据并强制下载到浏览器,而不向最终用户显示真实的URL。

答案 2 :(得分:2)

有许多不同的方法可以解决这个问题。最简单的可能只是创建一个数据库支持的模型,该模型存储对pdf和随机id的引用。然后,show动作将是/obscured_pdf/asdkfjlkdafj1230-5324.pdf格式的链接或您提出的任何随机ID。所以对于一个基本的例子:

控制器

class ObscuredPdfsController < ApplicationController
  def show
    @pdf = ObscuredPdf.find_by_obscured_id(params[:id]).pdf
    # render pdf, do whatever
  end
end

在routes.rb

resources :obscured_pdfs, only: :show

模糊的课程

class ObscuredPdf < ActiveRecord::Base
  belongs_to :pdf
  belongs_to :user
  attr_accessible :obscured_id
end

更好的是,将obscured_id字段添加到pdf类中,但如果多个人需要不同的链接指向同一个pdf,则这不起作用。就个人而言,我最喜欢的选项是根据user_idpdf_id对称加密“随机”ID,这样您根本不需要存储它。