我是Rails的新手,遇到一个我不明白的奇怪问题。 我使用ActiveRecord作为会话存储,需要添加会话ID作为所有请求的JSON响应的属性。如果它对情况有一些影响,我也会使用Devise。问题是,如果用户在没有cookie的情况下发出请求(或者至少没有cookie中的会话ID),则session.id为空或者 - 请注意 - 不是响应cookie中设置的相同值。 / p>
为了进行调试,我将此代码作为after_filter添加到ApplicationController:
puts session.id
puts request.session_options[:id]
两个值都相同。它们与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
我的问题:如何在呈现第一个响应之前获取新会话的实际session.id值?
UPD:
我刚创建了一个新的Rails应用程序,该应用程序使用不带Devise的ActiveRecord会话存储,我可以获得会在使用此代码ID应用程序控制器响应之前在cookie中设置的session.id:
class ApplicationController < ActionController::Base
after_filter :show_session
def show_session
puts session.id
end
end
但是在我使用Devise的现有应用程序中,我得到一个看起来像会话ID的值,但是它与通过Set-Cookie响应头设置的cookie中的值不匹配,并且实际保存到数据库中的会话表的值。看起来Devise在某种程度上与ActiveRecord会话存储有冲突。需要更深入地了解它。
UPD 2
看起来我发现问题的根源。正如我所说,我使用Devise与Omniauth进行授权。根据文档,sign_in方法出于安全原因重置会话ID。但是在重置之后session.id返回已经自动设置的旧值。我将此代码用作Omniauth回调:
def facebook_access_token
sign_in @user
puts session.id
end
在控制台中,我得到的会话ID与Set-Cookie响应头中设置的会话ID不同。如果我评论“sign_in”行,则这些值匹配。新问题:如何在sign_in方法内重置后获取新的会话ID值?它是内部Warden / Devise实现还是什么?
答案 0 :(得分:3)
续订仍然很重要,你不应该禁用它
此外,新的会话ID是在执行控制器之后生成的,因此在您有机会将响应设置为发送给客户端之后。
解决方案是手动触发更新会话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
然后在您的控制器中,您只需在获取响应的会话ID之前调用此方法
def my_action
...
commit_session_now!
render json: {session_id: session.id}, status: :ok
end
commit_session_now!
中的代码来自Rack::Session::Abstract::ID#commit_session
https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb#L327
答案 1 :(得分:2)
我遇到的问题是由默认的Warden配置引起的。它更新了会话ID,但不知何故,新的id无法通过session.id访问。
我发现停止此行为的唯一方法是将此代码放入config / initializers / devise.rb中:
Warden::Manager.after_set_user do |user,auth,opts|
auth.env["rack.session.options"][:renew] = false
end
出于安全原因,这种方法可能并不是很好,但在一周的搜索和阅读资源中我没有其他想法。
答案 2 :(得分:0)
在不知道应用程序的详细信息的情况下,我的建议是在ApplicationController中使用before_filter
:
class ApplicationController < ActionController::Base
before_filter :use_session_id
protected
def use_session_id
# Do something with session.id
# This will get called before any rendering happens
end
end