4

How can one interact with multiple sessions cookies (for different path or domains) in a single rack application?

For example, considering the following application using 3 locations:

  • www.my-app.net => main app
  • www.my-app.net/app_a => sub app A
  • app_b.my_app.net/ => sub app B

Should be able to interact with 3 sessions cookies:

  • domain=www.my-app.net; path=/;
  • domain=www.my-app.net; path=/app_a;
  • domain=app_b.my-app.net/; path=/;

Rack::Session::Cookie seemed to be a good choice, but as a middleware the session cookie has to be set in config.ru and seems to be limited to one session cookie per rack application.

In this special case, the main rack application point is to easily add sub applications, so dividing the application in multiple rack application to use Rack::Session::Cookie is not a viable solution.

The ideal would be a way to interact freely with multiple session cookies from inside the rack application code.

For now, I am considering:

  • writing a middleware that allow interaction with multiple session cookies
  • use CGI::Cookie to implement a custom session cookie management inside the application

But both are quite tedious so I was wondering if there was a simpler way to achieve this functionality.

Thanks in advance for any advice or suggestion.

4

1 回答 1

3

如果有人有相同的需求,我发现创建一个类来管理应用程序内的会话是最简单的方法。

Rack::Utils 有 2 个不错的快捷方式,Rack::Utils.set_cookie_header!Utils.delete_cookie_header!在处理 cookie 时,这真的可以让事情变得更容易。

我将会话保存在使用我的应用程序的数据库中,但支持另一个后端应该很简单。

作为旁注,我提出了一些注意事项:

  • 为了确保 cookie 的名称有效,我为此使用了子应用程序名称的 sha-1。
  • SecureRandom.urlsafe_base64对于生成会话密钥很有用。
  • 会话清理和刷新必须手动实现。

示例代码

管理 cookie 的类,提交函数设置和删除要存储的 cookie。

class Framework::Response::Cookies

  def set( params )
    @cookies[params.delete( :name )] = params
  end

  def remove( params )
    @remove_cookies[params.delete( :name )] = params
  end

  def commit( headers )
    @cookies.each_pair do |name, params|
      Rack::Utils.set_cookie_header!( headers, name, params )
    end
    @remove_cookies.each_pair do |name, params|
      Rack::Utils.delete_cookie_header!( headers, name, params )
    end
  end

end

管理会话的类(使用 Mongo 作为后端):

class Database::Mongo::Session < Session

  def save
    expire = Time.now + @session_ttl
    @framework.content.db.find_and_modify( self.collection_name, { 
      :query => { :name => @session_name, :id => @session_id }, 
      :update => { :expire => expire, :name => @session_name, :id => @new_session_id || @session_id , :variables => @variables.to_hash },
      :upsert => true 
    })
    @framework.response.cookies.set( 
      :name => @session_name,
      :value => @new_session_id || @session_id,
      :path => @framework.applications.active.active_request['path'],
      :domain => @framework.applications.active.active_request['host'],
      :httponly => true  
    )
  end

  def delete
    @framework.content.db.remove( self.collection_name, { :id => @session_id } )
    @framework.response.cookies.remove( :name => @session_name )
  end

end

每次@framework.response.cookies.set调用时,它都会将 cookie 数据推送到Framework::Response::Cookies@cookies变量。

在提供响应之前,调用以Framework::Response::Cookies.commit使用Rack::Utils.

于 2012-11-05T02:28:18.827 回答