You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Ruby on Rails中实现Active Storage私有附件的安全访问?

实现Active Storage私有附件分发(带Token验证)

我正在用Ruby on Rails开发RESTful API,采用Active Storage管理附件,但Active Storage默认是公开分发附件,我希望实现私有分发,通过/assets/:name?token=TOKEN这样的路由来提供访问,前端调用方式如下:

// Front-end
const flowerPhoto = new Image()
const params = new URLSearchParams({ token })
flowerPhoto.src = `${API_URL}/assets/flower-photo?${params.toString()}`

查看Rails指南时,以下内容让我感到困惑:

所有Active Storage控制器默认公开可访问。生成的URL虽难以猜测,但设计上是永久有效的。如果你的文件需要更高等级的保护,请考虑实现认证控制器。
RedirectController会重定向到实际的服务端点。这种间接方式将服务URL与实际URL解耦,例如允许在不同服务中镜像附件以实现高可用性。重定向的HTTP过期时间为5分钟。

我尝试了以下实现:

路由配置

# config/routes.rb
Rails.application.routes.draw do
  scope 'assets/:name', as: 'asset' do
    root 'assets#show', as: ''
  end
end

模型定义

# app/models/asset.rb
class Asset < ApplicationRecord
  has_one_attached :document
end

控制器实现

# app/controllers/assets_controller.rb
class AssetsController < ApplicationController
  include ActiveStorage::SetCurrent

  before_action :set_asset
  before_action :authorize

  def show
    redirect_to @asset.document.url
  end

  private

  def set_asset = @asset = Asset.find_by!(name: params[:name])

  def authorize = user_authentication(params[:token])
end

关闭Active Storage默认路由

# config/application.rb
module App
  class Application < Rails::Application
    # Public attachment URLs
    config.active_storage.draw_routes = false
  end
end

但运行后遇到如下错误:

NoMethodError in AssetsController#show
undefined method `rails_disk_service_url' for module
redirect_to @asset.document.url

问题分析与解决方案

错误原因

关闭config.active_storage.draw_routes = false后,Active Storage的内置路由(包括生成签名URL所需的服务端逻辑)被禁用,导致url方法无法正常生成地址。另外,私有附件需要使用签名临时URL,而非永久公开URL,同时要确保存储服务配置为私有。

分步解决

1. 配置私有存储服务

修改config/storage.yml,将存储服务设置为私有(以本地Disk服务为例):

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>
  public: false # 核心设置:标记存储为私有

如果使用S3等云存储,需对应设置私有权限,比如S3添加acl: private。

2. 恢复Active Storage内置路由

删除config/application.rb中的config.active_storage.draw_routes = false,保留默认的路由生成,因为Active Storage需要这些路由来处理签名URL和重定向逻辑。

调整自定义路由为更简洁的形式:

# config/routes.rb
Rails.application.routes.draw do
  get '/assets/:name', to: 'assets#show', as: 'asset'
end

3. 修正控制器逻辑

使用签名临时URL来提供私有附件访问,有两种实现方式:

方式一:直接生成存储服务的签名URL
# app/controllers/assets_controller.rb
class AssetsController < ApplicationController
  include ActiveStorage::SetCurrent

  before_action :set_asset
  before_action :authorize

  def show
    # 生成有效期5分钟的签名URL,可自定义expires_in参数(单位秒)
    redirect_to @asset.document.service_url(expires_in: 300), allow_other_host: true
  end

  private

  def set_asset
    @asset = Asset.find_by!(name: params[:name])
  end

  def authorize
    # 替换为你的Token验证逻辑,示例如下
    unless valid_token?(params[:token])
      render json: { error: '未授权访问' }, status: :unauthorized
    end
  end

  # 自定义Token验证方法,根据业务需求实现
  def valid_token?(token)
    # 示例:验证Token是否关联有效用户
    # User.exists?(api_token: token)
  end
end
方式二:通过Active Storage RedirectController重定向(推荐)

这种方式符合Rails设计,解耦存储服务与应用路由:

def show
  # 生成指向RedirectController的签名URL,自动处理过期与重定向
  redirect_to rails_blob_path(@asset.document, disposition: 'inline'), allow_other_host: true
end

4. 确保附件以私有方式附加(可选)

如果全局存储服务未设为私有,可在模型中明确指定私有服务:

# app/models/asset.rb
class Asset < ApplicationRecord
  has_one_attached :document, service: :local # 指定使用私有存储服务
end

内容的提问来源于stack exchange,提问作者Chiara Ani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 22:21:09