为什么rails不断发回Set-Cookie标头?

时间:2017-02-04 18:37:37

标签: ruby-on-rails session cookies amazon-elb

我遇到了关于Cookie和会话在弹道和客户端之间混淆的弹性负载均衡器和清漆缓存问题。部分问题是,rails正在添加一个" Set-Cookie"几乎每个请求都有一个会话ID。如果客户端已经在发送session_id,并且它匹配rails将要设置的session_id ..为什么rails会不断地告诉客户"哦是啊..你的会话ID是......"

2 个答案:

答案 0 :(得分:9)

摘要: Set-Cookie标题几乎都在每个回复中设置,因为

  1. 默认会话存储将尝试将会话数据写入已访问会话的任何请求的加密cookie(要么从中读取或写入它),
  2. 即使纯文本值没有,
  3. 也会更改加密值
  4. 加密发生在它到达负责检查cookie值是否已更改以避免冗余Set-Cookie标头的代码之前。
  5. 纯文本cookie

    在Rails中,ActionDispatch::Cookies中间件负责根据Set-Cookie的内容编写ActionDispatch::Cookies::CookieJar响应标头。

    正常行为是您所期望的:如果cookie的值没有从请求的Cookie标头中更改,并且有效期未更新,则Rails将不会发送响应中新的Set-Cookie标头。

    这由CookieJar#[]=中的条件处理,它将已存储在cookie jar中的值与正在写入的新值进行比较。

    加密的cookie

    为了处理加密的cookie,Rails提供了ActionDispatch::Cookies::EncryptedCookieJar类。

    EncryptedCookieJar依赖于ActiveSupport::MessageEncryptor来提供加密和解密,每次调用时都使用随机initialisation vector。这意味着即使给出相同的纯文本字符串,它几乎可以保证返回不同的加密字符串。换句话说,如果我解密我的会话数据,然后重新加密它,我最终会得到一个与我开始的字符串不同的字符串。

    EncryptedCookieJar并没有做太多的事情:它包装了一个普通的CookieJar,只是在数据进入时提供加密,并在数据恢复时进行解密。这意味着CookieJar#[]=方法仍然负责检查cookie的值是否已更改,并且它甚至不知道已经给出的值已加密。

    EncryptedCookieJar的这两个属性解释了为什么设置加密Cookie而不更改其值将始终产生Set-Cookie标题。

    会话商店

    Rails提供不同的会话存储。其中大多数将会话数据存储在服务器上(例如,在memcached中),但默认值为ActionDispatch::Session::CookieStore - 使用EncryptedCookieJar将所有数据存储在加密的cookie中。

    ActionDispatch::Session::CookieStore#commit_session?继承Rack::Session::Abstract::Persisted方法,该方法确定是否应设置Cookie。如果会话被加载,那么答案几乎总是“是的,设置cookie”。

    正如我们已经看到的那样,在会话已加载但未更改的情况下,我们仍将以不同的加密值结束,因此标题为Set-Cookie

答案 1 :(得分:0)

请参阅@georgebrock的答案以了解发生这种情况的原因。修补rails来更改此行为以仅在会话更改时设置cookie相当容易。只需将此代码放在初始化程序目录中即可。

require 'rack/session/abstract/id' # defeat autoloading
module ActionDispatch
  class Request
    class Session # :nodoc:
      def changed?;@changed;end
      def load_for_write!
        load! unless loaded?
        @changed = true
      end
    end
  end
end

module Rack
  module Session
    module Abstract
      class Persisted
        private
        def commit_session?(req, session, options)
          if options[:skip]
            false
          else
            has_session = session.changed? || forced_session_update?(session, options)
            has_session && security_matches?(req, options)
          end
        end
      end
    end
  end
end