在 Rails 子域之间共享会话(cookie) ?

我有一个应用程序设置,其中每个用户属于一个公司,该公司有一个子域名(我使用的是 basecamp 样式的子域名)。我现在面临的问题是,ails 正在创建多个 cookies (一个用于 lvh.me,另一个用于 subdomain.lvh.me ) ,这导致我的应用程序中出现了不少故障(例如,虽然所有请求一旦登录,flash 消息都会持久化)。

在我的/cofig/initilizers/session _ store. rb 文件中有这个:

AppName::Application.config.session_store :cookie_store, key: '_application_devise_session', domain: :all

域名: : 一切似乎是我在谷歌上找到的标准答案,但似乎对我不起作用。感谢你们的帮助!

52788 次浏览

As it turns outs 'domain: all' creates a cookie for all the different subdomains that are visited during that session (and it ensures that they are passed around between request). If no domain argument is passed, it means that a new cookie is created for every different domain that is visited in the same session and the old one gets discarded. What I needed was a single cookie that is persistent throughout the session, even when the domain changes. Hence, passing domain: "lvh.me" solved the problem in development. This creates a single cookie that stays there between different subdomains.

For anyone needing further explanation, this is a great link: http://excid3.com/blog/sharing-a-devise-user-session-across-subdomains-with-rails-3/

Did you try

AppName::Application.config.session_store :cookie_store, key: '_application_devise_session', domain: 'lvh.me'

)

basically we are saying have single cookie for base domain and just ignore sub domain..though this approach has some flaws still ...

For some reason replacing :all with the domain did not work (rails 3.2.11) for me. It took a piece of custom Middleware to fix it. A summary of that solution is below.

tl;dr: You need to write a custom Rack Middleware. You need add it into your conifg/environments/[production|development].rb. This is on Rails 3.2.11

Cookie sessions are usually stored only for your top level domain.

If you look in Chrome -> Settings -> Show advanced settings… -> Privacy/Content settings… -> All cookies and site data… -> Search {yourdomain.com} You can see that there will be separate entries for sub1.yourdomain.com and othersub.yourdomain.com and yourdomain.com

The challenge is to use the same session store file across all subdomains.

Step 1: Add Custom Middleware Class

This is where Rack Middleware comes in. Some relevant rack & rails resources:

Here is a custom class that you should add in the lib This was written by @Nader and you all should thank him

# Custom Domain Cookie
#
# Set the cookie domain to the custom domain if it's present
class CustomDomainCookie
def initialize(app, default_domain)
@app = app
@default_domain = default_domain
end


def call(env)
host = env["HTTP_HOST"].split(':').first
env["rack.session.options"][:domain] = custom_domain?(host) ? ".#{host}" : "#{@default_domain}"
@app.call(env)
end


def custom_domain?(host)
host !~ /#{@default_domain.sub(/^\./, '')}/i
end
end

Basically what this does is that it will map all of your cookie session data back onto the exact same cookie file that is equal to your root domain.

Step 2: Add To Rails Config

Now that you have a custom class in lib, make sure are autoloading it. If that meant nothing to you, look here: Rails 3 autoload

The first thing is to make sure that you are system-wide using a cookie store. In config/application.rb we tell Rails to use a cookie store.

# We use a cookie_store for session data
config.session_store :cookie_store,
:key => '_yourappsession',
:domain => :all

The reason this is here is mentioned here is because of the :domain => :all line. There are other people that have suggested to specify :domain => ".yourdomain.com" instead of :domain => :all. For some reason this did not work for me and I needed the custom Middleware class as described above.

Then in your config/environments/production.rb add:

config.middleware.use "CustomDomainCookie", ".yourdomain.com"

Note that the preceding dot is necessary. See "sub-domain cookies, sent in a parent domain request?" for why.

Then in your config/environments/development.rb add:

config.middleware.use "CustomDomainCookie", ".lvh.me"

The lvh.me trick maps onto localhost. It's awesome. See this Railscast about subdomains and this note for more info.

Hopefully that should do it. I honestly am not entirely sure why the process is this convoluted, as I feel cross subdomain sites are common. If anyone has any further insights into the reasons behind each of these steps, please enlighten us in the comments.

http://excid3.com/blog/sharing-a-devise-user-session-across-subdomains-with-rails-3/

"The part you want to watch out for here is that if you set :domain => :all like is recommend in some places, it simply won’t work unless you’re using localhost. :all defaults to a TLD length of 1, which means if you’re testing with Pow (myapp.dev) it won’t work either because that is a TLD of length 2."

In other words you need:

 App.config.session_store ... , :domain => :all, :tld_length => 2

Also a good idea to clear your cookies

I came across this while looking for the simplest way to set the cookie to be the root domain. It seems there is some misinformation about the :all option when passed as the domain option. For most domains, it will actually work as expected, setting the cookie to the root domain (e.g. .example.com for test.example.com). I think most people experienced issues since they're using the domain lvh.me to test. The regex used by rails to find a top level domain is defined to be DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/. If you note the last part, you can see that rails interprets lvh.me as a TLD similar to com.au. If your use case needs lvh.me to work, then the :all option won't work properly, however, it appears to be the simplest and best option for most domains.

TL;DR, the correct answer here, assuming you aren't developing on a 3 letter domain (or any domain that confuses the above regex) is to use :all.

I was looking for a way to solve this problem without having to explicitly state the domain name, so I could hop between localhost, lvh.me, and whichever domains I would use in production without having to keep editing the session_store.rb file. However, setting "domain: :all" didn't seem to be working for me.

Ultimately I found that I needed to state the tld_length (top level domain length) in that expression. The default tld_length is 1 while example.lvh.me has a tld_length of 2 and 127.0.0.1.xip.io has a tld_length of 5, for example. So what I had in the session_store.rb file for subdomains on lvh.me in development and whatever else in production was the below.

MyApp::Application.config.session_store :cookie_store, key: '_MyApp_session', domain: :all, tld_length: 2

Hope this helps someone, as it took me a long time to find this answer!

Rails 4.x (also should be fine with Rails 5/6 versions)

How to get lvh.me:3000 and subdomain in localhost (Rails)

Development: I have shared cookies to adding .lvh.me into session_store.rb,

It will be shared between subdomains on localhost admin.lvh.me:3000, lvh.me:3000 and so on...

#config/initializers/session_store.rb


domain = Rails.env.production? ? ".domain_name.com" : ".lvh.me"


Rails.application.config.session_store :cookie_store,
key: '_app_name_session', domain: domain

support rails5

if you want it works with any domain:

Rails.application.config.session_store :cookie_store, key: '_my_app_session', domain: :all, tld_length: 2

To configure per environment you could use the following:

Rails.application.config.session_store :cookie_store, key: '_my_app_session', domain: {
production: '.example.com',
development: '.example.dev'
}.fetch(Rails.env.to_sym, :all)

Ref: https://github.com/plataformatec/devise/wiki/How-To:-Use-subdomains

If you are using Redis for session store.

if Rails.env.development?
Rails.application.config.session_store :redis_store, {
servers: [
{ host: 'localhost', port: 6379},
],
key: '_app_session',
expire_after: 1.day,
domain: :all
}


else
Rails.application.config.session_store :redis_store, {
servers: [
{ host: HOST_URL, port: PORT},
],
key: '_app_session',
expire_after: 1.day,
domain: '.domain.com',
tld_length: 2
}
    

end