Partager la session (cookies) entre les sous-domaines dans Rails?

92

J'ai une configuration d'application où chaque utilisateur appartient à une entreprise, et cette entreprise a un sous-domaine (j'utilise des sous-domaines de style basecamp). Le problème auquel je suis confronté est que les rails créent plusieurs cookies (un pour lvh.me et un autre pour subdomain.lvh.me), ce qui provoque de nombreuses interruptions dans mon application (comme les messages flash persistants dans toutes les demandes une fois connecté).

J'ai ceci dans mon fichier /cofig/initilizers/session_store.rb:

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

Le domaine:: tout semble être la réponse standard que j'ai trouvée sur Google, mais cela ne semble pas fonctionner pour moi. Toute aide est appréciée!

Wahaj Ali
la source

Réponses:

74

Comme il s'avère "domaine: tout" crée un cookie pour tous les différents sous-domaines qui sont visités pendant cette session (et il garantit qu'ils sont transmis entre les demandes). Si aucun argument de domaine n'est passé, cela signifie qu'un nouveau cookie est créé pour chaque domaine différent visité dans la même session et l'ancien est supprimé. Ce dont j'avais besoin était un cookie unique persistant tout au long de la session, même lorsque le domaine change. Par conséquent, le passage a domain: "lvh.me"résolu le problème du développement. Cela crée un cookie unique qui y reste entre différents sous-domaines.

Pour tous ceux qui ont besoin d'explications supplémentaires, voici un excellent lien: http://excid3.com/blog/sharing-a-devise-user-session-across-subdomains-with-rails-3/

Wahaj Ali
la source
2
Merci mec .. J'ai été confronté à ce problème sur l'un de mes projets .. Enfin trouvé la solution ..
Shirjeel Alam
3
Assurez-vous d'utiliser la même chose config.secret_key_basedans toutes vos applications, sinon il ne pourra pas décoder le cookie.
Bruno Buccolo
5
Je ne vois aucune question à ce sujet pour Rails 4. Savez-vous si cela a changé. Je ne peux pas le faire fonctionner avec mon projet. Il recrée sans cesse les cookies. Merci.
Andy
Que faire si je veux utiliser CacheStorepour stocker des sessions dans Memcached?
Amit Patel
2
Avec Rails4, j'ai trouvé que cela ne fonctionnait que pour les sous-domaines avec des tirets mais pas avec des traits de soulignement:Appname::Application.config.session_store :cookie_store, key: '_appname_session', domain: :all, tld_length: 2
user1515295
68

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

"La partie à laquelle vous voulez faire attention ici est que si vous définissez: domain =>: tout comme est recommandé à certains endroits, cela ne fonctionnera tout simplement pas à moins que vous n'utilisiez localhost.: La longueur par défaut du TLD est de 1 , ce qui signifie que si vous testez avec Pow (myapp.dev), cela ne fonctionnera pas non plus car c'est un TLD de longueur 2. "

En d'autres termes, vous avez besoin de:

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

Aussi une bonne idée pour effacer vos cookies

montréal
la source
1
C'est la meilleure réponse car le seul changement fonctionne dans tous les environnements (app.com et app.dev). Un middleware personnalisé n'est pas nécessaire. Bon point également pour effacer les cookies!
Turadg
1
vous manquez le, :tld_length => 2
montrealmike
1
Assurez-vous d'utiliser la même chose config.secret_key_basedans toutes vos applications, sinon il ne pourra pas décoder le cookie.
Bruno Buccolo
4
:domain => :allne fonctionnera pas dans Rails 4, essayez domain => 'lvh.me', tld_length = 2. Cela a fonctionné pour moi
Minh Triet
1
Avec Rails 4.2, j'ai obtenu de bons résultats domain: :all, tld_length: 2en utilisant simplement le lvh.medomaine.
zwippie
24

Je cherchais un moyen de résoudre ce problème sans avoir à indiquer explicitement le nom de domaine, afin que je puisse sauter entre localhost, lvh.me et tous les domaines que j'utiliserais en production sans avoir à continuer à éditer le fichier session_store.rb. Cependant, la configuration de "domain:: all" ne semblait pas fonctionner pour moi.

En fin de compte, j'ai trouvé que je devais indiquer le tld_length (longueur du domaine de premier niveau) dans cette expression. La valeur par défaut tld_length est 1 tandis que example.lvh.me a une tld_length de 2 et 127.0.0.1.xip.io a une tld_length de 5, par exemple. Donc, ce que j'avais dans le fichier session_store.rb pour les sous-domaines sur lvh.me en développement et tout ce qui était en production était le ci-dessous.

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

J'espère que cela aide quelqu'un, car il m'a fallu beaucoup de temps pour trouver cette réponse!

FangedParakeet
la source
19

Pour une raison quelconque, le remplacement :allpar le domaine n'a pas fonctionné (rails 3.2.11) pour moi. Il a fallu un morceau de middleware personnalisé pour le réparer. Un résumé de cette solution est ci-dessous.

tl; dr: Vous devez écrire un middleware de rack personnalisé. Vous devez l'ajouter dans votre fichier conifg/environments/[production|development].rb. C'est sur Rails 3.2.11

Les sessions de cookies sont généralement stockées uniquement pour votre domaine de premier niveau.

Si vous regardez dans Chrome -> Settings -> Show advanced settings… -> Privacy/Content settings… -> All cookies and site data… -> Search {yourdomain.com}Vous pouvez voir qu'il y aura des entrées séparées pour sub1.yourdomain.comet othersub.yourdomain.cometyourdomain.com

Le défi consiste à utiliser le même fichier de stockage de session dans tous les sous-domaines.

Étape 1: Ajouter une classe middleware personnalisée

C'est là qu'intervient Rack Middleware . Quelques ressources pertinentes sur les racks et les rails:

Voici une classe personnalisée que vous devriez ajouter dans le lib Ceci a été écrit par @Nader et vous devriez tous le remercier

# 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

En gros, cela mappera toutes les données de votre session de cookies sur le même fichier de cookies qui est égal à votre domaine racine.

Étape 2: Ajouter à la configuration des rails

Maintenant que vous avez une classe personnalisée dans lib, assurez-vous de la charger automatiquement. Si cela ne vous dit rien, regardez ici: Rails 3 autoload

La première chose à faire est de vous assurer que vous utilisez un magasin de cookies à l'échelle du système. Dans config/application.rbnous disons à Rails d'utiliser un magasin de cookies.

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

La raison pour laquelle cela est mentionné ici est à cause de la :domain => :allligne. D'autres personnes ont suggéré de spécifier :domain => ".yourdomain.com"au lieu de:domain => :all . Pour une raison quelconque, cela n'a pas fonctionné pour moi et j'avais besoin de la classe Middleware personnalisée comme décrit ci-dessus.

Puis dans votre config/environments/production.rbajout:

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

Notez que le point précédent est nécessaire. Voir « cookies de sous-domaine, envoyés dans une demande de domaine parent? » Pour savoir pourquoi.

Puis dans votre config/environments/development.rbajout:

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

L'astuce lvh.me est mappée sur localhost. C'est génial. Voir ce Railscast sur les sous - domaines et cette note pour plus d'informations.

J'espère que cela devrait le faire. Honnêtement, je ne suis pas tout à fait sûr de savoir pourquoi le processus est si compliqué, car je pense que les sites de sous-domaines croisés sont courants. Si quelqu'un a d'autres informations sur les raisons de chacune de ces étapes, veuillez nous éclairer dans les commentaires.

Evan
la source
Existe-t-il un moyen de faire fonctionner cela avec plusieurs domaines de premier niveau? J'ai un produit qui fonctionne dans différents pays. Ici, nous supposons que le domaine par défaut est votredomaine.com, mais que se passerait-il s'il était censé fonctionner pour .be .sv .fr .com.br .com.ar et autres? Merci.
Marc Lainez
Je n'arrive tout simplement pas à faire fonctionner ça. Je développe dans les rails 4 et il semble que les rials ignorent doucement tout le code ci-dessus. Il ne veut tout simplement pas partager la session entre les sous-domaines.
Ole Henrik Skogstrøm
@ OleHenrikSkogstrøm Assurez-vous d'utiliser la même chose config.secret_key_basedans toutes vos applications, sinon il ne pourra pas décoder le cookie.
Bruno Buccolo
17

Je suis tombé sur cela en cherchant le moyen le plus simple de définir le cookie comme domaine racine. Il semble qu'il y ait une certaine désinformation sur l' :alloption lorsqu'elle est passée comme option de domaine. Pour la plupart des domaines, cela fonctionnera comme prévu, en définissant le cookie sur le domaine racine (par exemple .example.compour test.example.com). Je pense que la plupart des gens ont rencontré des problèmes car ils utilisent le domaine lvh.mepour tester. L'expression régulière utilisée par les rails pour rechercher un domaine de premier niveau est définie comme étant DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/. Si vous notez la dernière partie, vous pouvez voir que rails s'interprète lvh.mecomme un TLD similaire à com.au. Si votre cas d'utilisation doit lvh.mefonctionner, l' :alloption ne fonctionnera pas correctement, cependant, elle semble être l'option la plus simple et la meilleure pour la plupart des domaines.

TL; DR, la bonne réponse ici, en supposant que vous ne développiez pas sur un domaine à 3 lettres (ou tout domaine qui confond l'expression régulière ci-dessus) est d'utiliser :all.

cassanego
la source
Merci, cela m'a finalement aidé à comprendre pourquoi tant de réponses recommandaient un tld_length de 2 mais pourquoi je n'en avais pas besoin!
soupdog
Cette réponse doit être plus élevée. Merci Monsieur.
luca.busin
"lvh.me comme un TLD similaire à com.au" BTW Rails devrait en effet interpréter .me de la même manière que c'est aussi un domaine de pays (Monténégro).
mahemoff
7

Rails 4.x (devrait également convenir aux versions Rails 5/6)

Comment obtenir lvh.me:3000 et le sous-domaine dans localhost (Rails)

Développement: j'ai partagé des cookies pour ajouter .lvh.medans session_store.rb,

Il sera partagé entre les sous-domaines sur localhost admin.lvh.me:3000, lvh.me:3000et ainsi de suite ...

#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
la source
4

As-tu essayé

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

)

fondamentalement, nous disons avoir un cookie unique pour le domaine de base et ignorer simplement le sous-domaine ... bien que cette approche ait encore quelques défauts ...

Naveed
la source
1

rails de support5

si vous voulez que cela fonctionne avec n'importe quel domaine:

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

Pour configurer par environnement, vous pouvez utiliser les éléments suivants:

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

Réf: https://github.com/plataformatec/devise/wiki/How-To:-Use-subdomains

cgg5207
la source
0

Si vous utilisez Redis pour le magasin de session.

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 Autriche
la source