总结: Set-Cookie 几乎每个响应都设置了标头,因为
- 默认会话存储将尝试在访问会话的任何请求(读取或写入会话)上将会话数据写入加密的 cookie,
- 即使明文值没有变化,加密值也会发生变化,
- 加密发生在它到达负责检查 cookie 值是否已更改以避免冗余
Set-Cookie 标头的代码之前。
纯文本 cookie
在 Rails 中,ActionDispatch::Cookies 中间件负责根据 ActionDispatch::Cookies::CookieJar 的内容编写 Set-Cookie 响应标头。
正常行为是您所期望的:如果 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 从Rack::Session::Abstract::Persisted 继承#commit_session? 方法,该方法确定是否应设置cookie。如果会话已加载,那么答案几乎总是“是的,设置 cookie”。
正如我们已经看到的,在会话已加载但未更改的情况下,我们最终仍会得到不同的加密值,因此是 Set-Cookie 标头。