Rails Rails cookie for path

geekjj · 发布于 2015年09月21日 · 最后由 geekjj 回复于 2015年09月22日 · 1415 次阅读
96

Rails 使用 cookie 存储 session 的时候,是否可以指定多个 cookie 文件? 比如: session_store.rb 文件配置如下

Rails.application.config.session_store :cookie_store, { key: '_example_session_A', path: '/A', domain: nil }
Rails.application.config.session_store :cookie_store, { key: '_example_session_B', path: '/B', domain: nil }
Rails.application.config.session_store :cookie_store, { key: '_example_session_C', path: '/C', domain: nil }

但是服务器每次返回的 cookie 路径总是 /C。 由于项目中不适合使用子域名的方式,所以考虑使用相对路径来分隔 cookie, 但不知是否可行?

共收到 1 条回复
96

好吧,可能问的问题太简答了, 我自己来回答吧。顺便说一下对于rails 用 cookie 存储 session 的理解,如有不对,请及时纠正。

配置

正如很多人所说,相比其它策略,使用cookie来存储session是最简单的,只需要配置一个地方,而且很多情况下还是自动配置好的。

/config/initializers 中有一个叫 session_store.rb 的文件。这也就是唯一需要配置的地方。

Rails.application.config.session_store :CookieStore, { key: 'my_site', path: '/', domain: nil }, :expire_after => 30.minutes

这里我们配置了使用cookie存储session; cookie的key是 my_site, 路径是/ (也就是对整个网站有效);30分钟有效期

session 存储机制

说完配置,那么session究竟是怎样存储被存储到cookie中的呢?

我们先看一段代码

class CookieStore < Rack::Session::Abstract::Persisted
  ...
  def set_cookie(request, session_id, cookie)
    cookie_jar(request)[@key] = cookie
  end
  def get_cookie(req)
    cookie_jar(req)[@key]
  end
  ...

这是 Rails 代码中 CookieStore 代码的片段。set_cookie 发生在请求快要结束的时候,会将新的cookie值写入这次请求当中。@key这个实例变量里面存储的就是我们之前在配置文件/config/initializers/session_store.rb中指定的key。 而get_cookie发送在请求的早期,当服务器收到请求会从request中尝试去除@key对应的cookie值。

那么你一定会好奇了,cookie值里面存储了什么内容呢?

class CookieStore < Rack::Session::Abstract::Persisted
  ...
  def load_session(req)
    stale_session_check! do
      data = unpacked_cookie_data(req)
      data = persistent_session_id!(data)
      [data["session_id"], data]
    end
  end
  private
  def unpacked_cookie_data(req)
    req.fetch_header("action_dispatch.request.unsigned_session_cookie") do |k|
      v = stale_session_check! do
        if data = get_cookie(req)
          data.stringify_keys!
        end
        data || {}
      end
      req.set_header k, v
    end
  end
  def persistent_session_id!(data, sid=nil)
    data ||= {}
    data["session_id"] ||= sid || generate_sid
    data
  end
  def write_session(req, sid, session_data, options)
    session_data["session_id"] = sid
    session_data
  end
  ...

这里我们可以看到CookieStore将session_id 写入了我们的cookie值当中。每次加载session的时候会从request附带的cookie值中取出session_id重建session。从实际调试上面一段代码得到的结果看,CookieStore不仅将session_id放在了cookie值中,一同被放入的还有你在代码里面设置的所有session键值对。

这样看起来CookieStore的操作方式岂不是很危险?不用担心,CookieStore虽然将所有session内容存储到了cookie并放在客户端保管,但是它也对这些内容进行了加密处理,所以安全方面倒还过得去。

回归主题

好了,聊完这么多,我们还是回归我们的主题吧,如何在一个Rails应用中根据其链接的第一级子目录来分隔session呢?要分隔session也就是对于第一级子目录不同的微站要使用不同的cookie存储。而Rails多cookie存储方面在网上没有找到什么资料。我们可以做如下处理:

首先,我们要基于CookieStore定制我们自己的SampleCookieStore, 代码如下:

class ActionDispatch::Session::MyCookieStore < ActionDispatch::Session::CookieStore
  private
    def set_cookie(request, session_id, cookie)
      cookie[:path] = context_path(request)
      cookie_jar(request)[context_key(request)] = cookie
    end
    def get_cookie(req)
      cookie_jar(req)[context_key(req)]
    end
    def context_path(req)
      # 从request获取第一级子目录对应的相对路径,主要用作cookie的path
    end

    def context_key(req)
      # 从request获取第一级子目录对应的key,这里主要用来区分不同微站的cookie,防止冲突
    end
end

然后再SessionStore的配置文件里面指定使用我们自己的SampleCookieStore即可

Rails.application.config.session_store :SampleCookieStore, { key: 'my_site', path: '/', domain: nil }, :expire_after => 30.minutes

这样就可以把各个微站的session安全的分隔开了。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册