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

Ruby 1.8.7:禁止批量调用打印链接,仅允许按钮触发

针对你遇到的问题——禁止用户直接在浏览器地址栏输入URL调用create_value_zone_printout接口,只能通过页面按钮触发,结合你使用的Ruby 1.8.7和老版本Rails(从路由写法判断应该是Rails 2.x),我整理了几个实用的解决方案:

方案1:将请求从GET改为POST(最直接有效)

GET请求天生支持地址栏直接访问,改成POST后,用户无法通过输入URL触发请求,必须通过表单或带指定method的链接提交,从根源上解决问题。

修改步骤:

  1. 更新路由配置:把GET请求改为POST
map.resources :zones, :collection => {:create_value_zone_printout => :post}
  1. 修改视图中的按钮链接:给link_to添加:method => :post选项(Rails 2默认包含UJS脚本,支持这个功能)
<%= link_to 'sagatavot izdruku', 
            {:controller => "zones", :action => "create_value_zone_printout", :value_zone => @zone.zone_name}, 
            :class => "print", :method => :post %>
  1. 控制器新增请求方法校验:确保只有POST请求能进入逻辑
def create_value_zone_printout
  @service = "value_zones"
  if !public_user?
    return unless check_service_permissions
  end

  # 新增:校验请求方法为POST
  return permission_denied unless request.post?

  respond_to do |format|
    format.html do
      rep_id = Report.get_printout_file_id('VALUE_ZONE', session, {:value_zones => params[:value_zone]})
      unless rep_id.blank? or rep_id.is_a?(Array)
        return permission_denied(SilentError.new("Kļūda veidojot izdruku!", rep_id.to_s, true))
      end
      add_service_to_user_orders(nil, [params[:value_zone]], rep_id) unless public_user?
      file_data = Attachment.find_by_id(rep_id.first.keys.first)
      send_data(file_data.data_buffered, :filename => file_data.file_name)
      file_data.destroy if public_user?
    end
  end
end

方案2:添加一次性签名验证(适合无法修改请求方法的场景)

如果因为业务限制不能改成POST,可以给每个按钮的URL生成会话绑定的唯一签名,服务器端验证签名有效性,防止用户篡改或批量构造URL。

修改步骤:

  1. 视图中生成签名:基于value_zone和当前会话ID生成哈希签名(密钥建议存到配置文件)
<% 
  value_zone = @zone.zone_name
  secret_key = "your_secure_custom_secret_key"
  # Ruby 1.8.7用Digest生成SHA1签名
  signature = Digest::SHA1.hexdigest("#{value_zone}#{session[:session_id]}#{secret_key}")
%>
<%= link_to 'sagatavot izdruku', 
            {:controller => "zones", :action => "create_value_zone_printout", :value_zone => value_zone, :signature => signature}, 
            :class => "print" %>
  1. 控制器中验证签名:收到请求后重新计算签名,和参数中的签名对比,不一致则拒绝访问
def create_value_zone_printout
  @service = "value_zones"
  if !public_user?
    return unless check_service_permissions
  end

  # 签名验证逻辑
  value_zone = params[:value_zone]
  provided_signature = params[:signature]
  secret_key = "your_secure_custom_secret_key"
  expected_signature = Digest::SHA1.hexdigest("#{value_zone}#{session[:session_id]}#{secret_key}")
  
  unless provided_signature == expected_signature
    return permission_denied(SilentError.new("Nederīga pieeja!", "Invalid request signature", true))
  end

  respond_to do |format|
    format.html do
      rep_id = Report.get_printout_file_id('VALUE_ZONE', session, {:value_zones => params[:value_zone]})
      unless rep_id.blank? or rep_id.is_a?(Array)
        return permission_denied(SilentError.new("Kļūda veidojot izdruku!", rep_id.to_s, true))
      end
      add_service_to_user_orders(nil, [params[:value_zone]], rep_id) unless public_user?
      file_data = Attachment.find_by_id(rep_id.first.keys.first)
      send_data(file_data.data_buffered, :filename => file_data.file_name)
      file_data.destroy if public_user?
    end
  end
end

这个签名和当前会话绑定,用户复制URL到其他浏览器或会话过期后就无法使用,能有效防止批量调用。

方案3:Referer检查(辅助验证,不单独使用)

可以检查请求的Referer是否来自你的应用内页面,但这个方法不可靠(用户可以伪造Referer),所以只能作为上面方案的补充:

def create_value_zone_printout
  # ... 其他逻辑
  # 辅助校验Referer
  app_domain = "http://your_app_domain.com"
  unless request.referer && request.referer.starts_with?(app_domain)
    return permission_denied(SilentError.new("Nederīga pieeja!", "Invalid referer", true))
  end
  # ... 其他逻辑
end

优先推荐方案1,因为POST请求符合HTTP规范,实现简单且从根本上避免了直接地址栏访问的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:23:14