Bar*_*rdt 6 cookies session ruby-on-rails devise
我是Rails的新手,遇到了一个我不理解的奇怪问题。我将ActiveRecord用作会话存储,并且需要为所有请求添加会话ID作为JSON响应的属性。如果对情况有一定影响,我也会使用Devise。问题是,如果请求是由没有Cookie(或至少在Cookie中没有会话ID)的用户发出的,则session.id为空或-请注意-请不要在响应Cookie中设置相同的值。
为了进行调试,我将此代码作为after_filter添加到ApplicationController中:
puts session.id
puts request.session_options[:id]
Run Code Online (Sandbox Code Playgroud)
这两个值是相同的。它们匹配cookie中的值(如果存在)。否则,如果cookie中不存在会话ID,则在该请求之后设置的cookie具有不同的值。
我的意见是,session_id在实际保存到数据库之后必须获取新值,该数据库必须是唯一的。数据库迁移:
def change
create_table :sessions do |t|
t.string :session_id, :null => false
t.text :data
t.timestamps
end
add_index :sessions, :session_id, :unique => true
add_index :sessions, :updated_at
end
Run Code Online (Sandbox Code Playgroud)
我的问题:如何在呈现第一个响应之前获取新会话的实际session.id值?
UPD:
我刚刚创建了一个新的Rails应用程序,该应用程序使用的ActiveRecord会话存储没有Devise,我可以在要使用此代码ID应用程序控制器响应之前在cookie中设置session.id:
class ApplicationController < ActionController::Base
after_filter :show_session
def show_session
puts session.id
end
end
Run Code Online (Sandbox Code Playgroud)
但是在使用Devise的现有应用程序中,我得到的值确实看起来像会话ID,但与通过Set-Cookie响应标头在cookie中设置的值和实际保存到数据库中session表的值不匹配。看起来Devise在某种程度上与ActiveRecord会话存储有冲突。需要更深入地了解它。
UPD 2
看来我找到了问题的根源。就像我说的,我使用Devise对Omniauth进行授权。根据文档,出于安全原因,sign_in方法重置会话ID。但是,在该重置session.id返回旧值之后,该值已被自动设置。我将此代码用作Omniauth回调:
def facebook_access_token
sign_in @user
puts session.id
end
Run Code Online (Sandbox Code Playgroud)
在控制台中,我获得的会话ID与Set-Cookie响应标头中的会话ID不同。如果我在“ sign_in”行中注释,则这些值匹配。新问题:在sign_in方法内将新的会话ID值重置后,如何获取?它是内部的Warden / Devise实现还是其他?
续订仍然很重要,您不应该禁用它
此外,新的会话 ID 是在控制器执行后生成的,因此在您有机会设置要发送到客户端的响应之后。
解决办法是手动触发更新session id
在您的ApplicationController添加方法中:
protected
def commit_session_now!
return unless session.options[:renew]
object = session.options.instance_variable_get('@by')
env = session.options.instance_variable_get('@env')
session_id = object.send(:destroy_session, env, session.id || object.generate_sid, session.options)
session_data = session.to_hash.delete_if { |k,v| v.nil? }
object.send(:set_session, env, session_id, session_data, session.options)
session.options[:renew] = false
session.options[:id] = session_id
end
Run Code Online (Sandbox Code Playgroud)
然后在您的控制器中,您只需在获取响应的会话 ID 之前调用此方法
def my_action
...
commit_session_now!
render json: {session_id: session.id}, status: :ok
end
Run Code Online (Sandbox Code Playgroud)
中的代码commit_session_now!来自Rack::Session::Abstract::ID#commit_session https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb#L327
我遇到的问题是由默认的 Warden 配置引起的。它更新了会话 ID,但不知何故无法通过 session.id 访问新的 ID。
我发现阻止这种行为的唯一方法是将这段代码放入 config/initializers/devise.rb 中:
Warden::Manager.after_set_user do |user,auth,opts|
auth.env["rack.session.options"][:renew] = false
end
Run Code Online (Sandbox Code Playgroud)
出于安全原因,这种方法可能不太好,但在一周的搜索和阅读源代码中我没有其他想法。
| 归档时间: |
|
| 查看次数: |
3076 次 |
| 最近记录: |