Compartilhar sessão (cookies) entre subdomínios no Rails?

92

Eu tenho uma configuração de aplicativo em que cada usuário pertence a uma empresa e essa empresa tem um subdomínio (estou usando subdomínios no estilo basecamp). O problema que estou enfrentando é que o rails está criando vários cookies (um para lvh.me e outro para subdomain.lvh.me), o que está causando algumas interrupções em meu aplicativo (como mensagens flash persistentes apesar de todas as solicitações uma vez assinado).

Eu tenho isso em meu arquivo /cofig/initilizers/session_store.rb:

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

O domínio:: all parece ser a resposta padrão que encontrei no Google, mas não parece estar funcionando para mim. Qualquer ajuda é apreciada!

Wahaj Ali
fonte

Respostas:

74

Acontece que 'domínio: todos' cria um cookie para todos os diferentes subdomínios que são visitados durante aquela sessão (e garante que eles sejam passados ​​entre as solicitações). Se nenhum argumento de domínio for passado, significa que um novo cookie é criado para cada domínio diferente que é visitado na mesma sessão e o antigo é descartado. O que eu precisava era de um único cookie que fosse persistente durante toda a sessão, mesmo quando o domínio fosse alterado. Portanto, a aprovação domain: "lvh.me"resolveu o problema no desenvolvimento. Isso cria um único cookie que permanece lá entre diferentes subdomínios.

Para quem precisa de mais explicações, este é um ótimo link: http://excid3.com/blog/sharing-a-devise-user-session-across-subdomains-with-rails-3/

Wahaj Ali
fonte
2
Obrigado cara .. Estou enfrentando esse problema em um dos meus projetos .. Finalmente encontrei a solução ..
Shirjeel Alam
3
Certifique-se de usar o mesmo config.secret_key_baseem todos os seus aplicativos, caso contrário, não será possível decodificar o cookie.
Bruno Buccolo
5
Não vejo nenhuma pergunta relacionada a isso para Rails 4. Você sabe se isso mudou. Não consigo fazer funcionar com meu projeto. Ele continua recriando os cookies. Obrigado.
Andy
E se eu quiser usar CacheStorepara armazenar sessões no memcached?
Amit Patel
2
Com Rails4, descobri que isso funcionava apenas para subdomínios com traços, mas não com sublinhados:Appname::Application.config.session_store :cookie_store, key: '_appname_session', domain: :all, tld_length: 2
usuário1515295
68

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

"A parte que você quer observar aqui é que se você definir: domain =>: all like é recomendado em alguns lugares, ele simplesmente não funcionará a menos que você esteja usando localhost.: Todos os padrões para um comprimento de TLD de 1 , o que significa que se você estiver testando com Pow (myapp.dev), também não funcionará porque é um TLD de comprimento 2. "

Em outras palavras, você precisa:

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

Também é uma boa ideia limpar seus cookies

montrealmike
fonte
1
Esta é a melhor resposta porque a única alteração funciona em todos os ambientes (app.com e app.dev). O middleware customizado não é necessário. Também é um bom ponto para limpar os cookies!
Turadg
1
você está perdendo o, :tld_length => 2
montrealmike
1
Certifique-se de usar o mesmo config.secret_key_baseem todos os seus aplicativos, caso contrário, não será possível decodificar o cookie.
Bruno Buccolo
4
:domain => :allnão funcionará no Rails 4, tente domain => 'lvh.me', tld_length = 2. Funcionou para mim
Minh Triet,
1
Com Rails 4.2 obtive bons resultados apenas domain: :all, tld_length: 2usando o lvh.medomínio.
zwippie
24

Eu estava procurando uma maneira de resolver esse problema sem precisar declarar explicitamente o nome de domínio, para que pudesse alternar entre localhost, lvh.me e quaisquer domínios que usaria na produção sem ter que continuar editando o arquivo session_store.rb. No entanto, definir "domínio:: all" não parece estar funcionando para mim.

Por fim, descobri que precisava declarar o tld_length (comprimento do domínio de nível superior) nessa expressão. O tld_length padrão é 1, enquanto example.lvh.me tem tld_length de 2 e 127.0.0.1.xip.io tem tld_length de 5, por exemplo. Então, o que eu tinha no arquivo session_store.rb para subdomínios em lvh.me em desenvolvimento e tudo o mais em produção era o abaixo.

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

Espero que isso ajude alguém, pois demorei muito para encontrar essa resposta!

FangedParakeet
fonte
19

Por algum motivo, a substituição :allcom o domínio não funcionou (rails 3.2.11) para mim. Foi preciso um middleware personalizado para consertar. Um resumo dessa solução está abaixo.

tl; dr: Você precisa escrever um Middleware de Rack personalizado. Você precisa adicioná-lo ao seu conifg/environments/[production|development].rb. Isso está no Rails 3.2.11

As sessões de cookies geralmente são armazenadas apenas para o seu domínio de nível superior.

Se você olhar em Chrome -> Settings -> Show advanced settings… -> Privacy/Content settings… -> All cookies and site data… -> Search {yourdomain.com}você pode ver que haverá entradas separadas para sub1.yourdomain.come othersub.yourdomain.comeyourdomain.com

O desafio é usar o mesmo arquivo de armazenamento de sessão em todos os subdomínios.

Etapa 1: adicionar classe de middleware personalizada

É aqui que entra o Rack Middleware . Alguns recursos de rack e trilhos relevantes:

Aqui está uma classe personalizada que você deve adicionar no lib This foi escrito por @Nader e todos vocês devem agradecê-lo

# 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

Basicamente, o que isso faz é mapear todos os dados da sua sessão de cookie de volta para o mesmo arquivo de cookie que é igual ao seu domínio raiz.

Etapa 2: Adicionar à configuração do Rails

Agora que você tem uma classe personalizada em lib, certifique-se de carregá-la automaticamente. Se isso não significava nada para você, veja aqui: autoload do Rails 3

A primeira coisa é certificar-se de que você está usando um armazenamento de cookies em todo o sistema. Em config/application.rbnós dizemos ao Rails para usar um armazenamento de cookies.

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

A razão pela qual isso está aqui é mencionado aqui é por causa da :domain => :alllinha. Outras pessoas sugeriram especificar em :domain => ".yourdomain.com"vez de:domain => :all . Por algum motivo, isso não funcionou para mim e eu precisava da classe de Middleware personalizada conforme descrito acima.

Então, em seu config/environments/production.rbadd:

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

Observe que o ponto anterior é necessário. Consulte " cookies de subdomínio, enviados em uma solicitação de domínio pai? " Para saber por quê.

Então, em seu config/environments/development.rbadd:

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

O truque do lvh.me mapeia para localhost. É incrivel. Veja este Railscast sobre subdomínios e esta nota para mais informações.

Esperançosamente isso deve bastar. Sinceramente, não tenho certeza de por que o processo é tão complicado, pois acho que sites de subdomínios cruzados são comuns. Se alguém tiver mais informações sobre as razões por trás de cada uma dessas etapas, esclareça-nos nos comentários.

Evan
fonte
Existe uma maneira de fazer isso funcionar com vários domínios de nível superior? Tenho um produto que funciona em diferentes países. Aqui, estamos assumindo que o domínio padrão é seudominio.com, mas e se ele deveria funcionar para .be .sv .fr .com.br .com.ar e outros? Obrigado.
Marc Lainez
Eu simplesmente não consigo fazer isso funcionar. Estou desenvolvendo no rails 4 e parece que rials apenas ignora suavemente todo o código acima. Ele simplesmente não deseja compartilhar a sessão entre subdomínios.
Ole Henrik Skogstrøm,
@ OleHenrikSkogstrøm Certifique-se de usar o mesmo config.secret_key_baseem todos os seus aplicativos, ou então não será capaz de decodificar o cookie.
Bruno Buccolo
17

Eu me deparei com isso enquanto procurava a maneira mais simples de definir o cookie como o domínio raiz. Parece que há alguma desinformação sobre a :allopção quando passada como opção de domínio. Para a maioria dos domínios, ele realmente funcionará conforme o esperado, configurando o cookie para o domínio raiz (por exemplo, .example.compara test.example.com). Acho que a maioria das pessoas teve problemas porque estão usando o domínio lvh.mepara testar. O regex usado por rails para encontrar um domínio de nível superior é definido como DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/. Se você observar a última parte, verá que o rails é interpretado lvh.mecomo um TLD semelhante a com.au. Se o seu caso de uso precisar lvh.mefuncionar, a :allopção não funcionará corretamente; no entanto, parece ser a opção mais simples e melhor para a maioria dos domínios.

TL; DR, a resposta correta aqui, assumindo que você não está desenvolvendo em um domínio de 3 letras (ou qualquer domínio que confunda o regex acima) é usar :all.

cassanego
fonte
Obrigado, isso finalmente me ajudou a entender por que tantas respostas recomendavam um tld_length de 2, mas por que eu não precisava!
soupdog de
Essa resposta precisa ser superior. Obrigado, senhor.
luca.busin
"lvh.me como um TLD semelhante a com.au" BTW Rails deve realmente interpretar .me da mesma forma que também é um domínio de país (Montenegro).
mahemoff
7

Rails 4.x (também deve funcionar nas versões Rails 5/6)

Como obter lvh.me:3000 e subdomínio em localhost (Rails)

Desenvolvimento: Eu compartilhei cookies para acrescentar .lvh.meem session_store.rb,

Ele será compartilhada entre subdomínios em localhost admin.lvh.me:3000, lvh.me:3000e assim por diante ...

#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
7urkm3n
fonte
4

Você tentou

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

)

basicamente, estamos dizendo para ter um único cookie para o domínio base e apenas ignorar o subdomínio ... embora essa abordagem ainda tenha algumas falhas ...

Naveed
fonte
1

trilhos de suporte 5

se você quiser que funcione com qualquer domínio:

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

Para configurar por ambiente, você pode usar o seguinte:

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

cgg5207
fonte
0

Se você estiver usando Redis para armazenamento de sessão.

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 
Marcelo Austria
fonte