解决Rails与SWFUpload的兼容性问题
转自:http://www.bluedash.net/spaces/%E8%A7%A3%E5%86%B3Rails%E4%B8%8ESWFUpload%E7%9A%84%E5%85%BC%E5%AE%B9%E6%80%A7%E9%97%AE%E9%A2%98
SWFUpload 这个工具目前非常流行,由于它支持队列上传,并支持进度显示,并可以通过Flash在客户端对文件进行验证和尺寸限制,对用户比较友好,所以很多网站都使用它做为文件上传工具。
但当它遇到Rails框架时,却出现了水土不服。由于Rails框架不再支持非Cookie的方式解析Session,导致SWFUpload在与Rails结合使用时,会出现Flash对象无法正常传递Session的情况。
因此不光是SWFUpload,所有的Flash项目在Rails框架下应用都会遇到相同的问题。当Rails网站使用 protect_from_forgery
进行保护时,如果不能正确地传递Session数据,那么Rails将会报错:ActionController::InvalidAuthenticityToken
这是由于Rails框架取不到Session数据里面的认证信息造成的。一个粗糙的解决方法是在 Controller
的一开始加入下面的代码:skip_before_filter
:verify_authenticity_token
,
:only
=> [...
但这样做等于是打开了某些方法的后门,形成安全漏洞。更标准的解决办法应该是利用Rails的Rack Middleware机制,将Flash对象的数据传输过程注入Session。详细过程如下:
首先在 app
目录下建立 middleware/flash_session_cookie_middleware.rb
:
require 'rack/utils'
class FlashSessionCookieMiddleware
def initialize(app, session_key = '_session_id')
@app = app
@session_key = session_key
end
def call(env)
if env['HTTP_USER_AGENT'] =~ /^(Adobe|Shockwave) Flash/
params = ::Rack::Utils.parse_query(env['QUERY_STRING'])
env['HTTP_COOKIE'] = [ @session_key, params[@session_key] ].join('=').freeze unless params[@session_key].nil?
env['HTTP_ACCEPT'] = '*/*'
end
@app.call(env)
end
end
仔细阅读的话,代码很容易理解,当检测到HTTP通讯为Flash对象时,从 QUERY_STRING
中取得Session ID,并将相关Cookie数据注入。
接下来是使这个Middleware生效,在 environment.rb
中添加如下代码:config.load_paths += %
W
(
#{RAILS_ROOT}/app/middleware)
使得这个Middleware在启动时加载。这样当我们在使用SWFUpload时,把Session ID通过查询字串传入即可:upload_url:
"/your_url?_your_session<%=cookies[:_your_session]%>&authenticity_token=<%=form_authenticity_token.inspect%>
其中 _your_session
是网站Session的特征串,可在 initializers/session_store.rb
中查到。
1 本文中用到的 FlashSessionCookieMiddleware
来自于 http://thewebfellas.com/blog/2008/12/22/flash-uploaders-rails-cookie-based-sessions-and-csrf-rack-middleware-to-the-rescue ,稍有修改。
2 感谢刘必胜先生为本文的撰写提供技术帮助。
高版本rails参见:http://metautonomo.us/2010/07/09/uploadify-and-rails-3/