为什么 "expiry" header 没有随着 Rails 应用上对 Ruby 的每个新请求而更新?

Why isn't "expiry" header updated with each new request to Ruby on Rails app?

我正在使用 Devise :timeoutable 模块使 session 在闲置一段时间后过期。每个新请求都会重置超时,因此 session 到期日期会发生变化。但这并没有反映在响应客户端的 expiry header 中。

我注意到 expiry 响应 header 对于我发送到服务器的所有 XHR 都是不变的。即使刷新页面后,expiry header 也不会更新。换句话说,header 的值仅对我在 sign-in 之后发送的第一个请求是正确的,然后它保持不变。

没有相关代码可以演示,因为 Devise 将 auth header 附加到开箱即用的每个响应。

下面是 响应 headers 的样子:

access-token: qK5XalnvENIXJDHGCKJ5_A
Cache-Control: no-cache
client: c-4_msWmyUFD7PwD7fPW5Q
Connection: keep-alive
Content-Encoding: gzip
Content-Type: application/json; charset=utf-8
Date: Mon, 15 Apr 2019 15:52:32 GMT
expiry: 1556548396
Server: nginx
token-type: Bearer
Transfer-Encoding: chunked
uid: user@email.com
Vary: Origin
X-Request-Id: 2fa70ebf-dcf2-4680-8c25-68d7b5e12b1b
X-Runtime: 0.021004

请求headers(如果相关):

GET /api/user/context HTTP/1.1
Host: qa.whiz.ai:9060
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
access-token: qK5XalnvENIXJDHGCKJ5_A
client: c-4_msWmyUFD7PwD7fPW5Q
Accept: application/json, text/plain, */*
expiry: 1556548396
uid: user@email.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36
token-type: Bearer
Referer: https://my-page.example.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,uk-UA;q=0.8,uk;q=0.7,ru-UA;q=0.6,ru;q=0.5
Cookie: ...

我尝试从请求 header 中删除 expiry,但没有任何效果。

这是预期的行为吗? Devise 不应该立即发送 header 中的实际值吗?

我想知道实际的到期时间,以便能够在客户端上执行一些操作。为此,我可以尝试描述 here 的解决方案,但如果我可以利用每个响应中已经存在的 header 会简单得多。

设计超时模块与到期header完全无关。仅仅因为两个概念可能看起来相似并不意味着它们是相关的。

Timeoutable 将 last_request_at 存储在 session 中(默认情况下是 cookie 存储)。

您可以通过以下方式获取值:

ENV['warden'].session(scope)['last_request_at']

其中 scope 基于您的模型名称。

来自the source

Warden::Manager.after_set_user do |record, warden, options|
  scope = options[:scope]
  env   = warden.request.env

  if record && record.respond_to?(:timedout?) && warden.authenticated?(scope) &&
     options[:store] != false && !env['devise.skip_timeoutable']
    last_request_at = warden.session(scope)['last_request_at']

    if last_request_at.is_a? Integer
      last_request_at = Time.at(last_request_at).utc
    elsif last_request_at.is_a? String
      last_request_at = Time.parse(last_request_at)
    end

    proxy = Devise::Hooks::Proxy.new(warden)

    if record.timedout?(last_request_at) &&
        !env['devise.skip_timeout'] &&
        !proxy.remember_me_is_active?(record)
      Devise.sign_out_all_scopes ? proxy.sign_out : proxy.sign_out(scope)
      throw :warden, scope: scope, message: :timeout
    end

    unless env['devise.skip_trackable']
      warden.session(scope)['last_request_at'] = Time.now.utc.to_i
    end
  end
end

过期时间 header 用于控制每个单独响应的缓存。客户端应该如何以及何时考虑指定响应过期的启发式方法非常复杂,但是到期时间 header 包含响应何时应该过期的时间戳。