2012-01-30 30 views
11

Tengo un servidor de nodo único que responde a las solicitudes y redirige a un usuario en función de los encabezados de host. El uso es que el sitio estático/home vive en www y cada usuario tiene su propio subdominio (es decir, www.example.com y site.example.com). El enrutamiento es según site.js.Uso de Express and Node, cómo mantener una sesión en subdominios/hostheaders

Cuando el usuario no ha iniciado sesión, se le redirige para iniciar sesión.

Estoy descubriendo que la sesión no se mantiene cuando el usuario es redirigido a su subdominio. Supongo que esto se espera, pero me pregunto si hay una forma de mantener la misma sesión en ambos subdominios.

Tenía la esperanza de que si estuvieran conectados y volvieran a www.example.com verían una vista diferente que incluyera un enlace para cerrar la sesión/su tablero, etc. Mi solución en este momento, estoy pensando, es simplemente crear la sesión en su subdominio y si vuelven a www será como si no hubieran iniciado sesión.

¿Alguien ha tratado esto antes o tiene respuestas sobre cómo manejar sesiones de esta manera?

creo que el problema puede estar en users.js donde vuelvo a dirigir a 'http://site.example.com' ya que no es una ruta relativa ...

Este es el código correspondiente (el usuario operaciones de búsqueda se realiza utilizando MongoDB y lo he dejado como su funcionamiento muy bien - la línea que llama a este servicio es users.authenticate) ...

server.js:

app.configure -> 
app.set "views", "#{__dirname}/views" 
app.set "view engine", "jade" 
app.use express.bodyParser() 
app.use express.methodOverride() 
app.use express.cookieParser() 
app.use express.session { 
    key: "KEY", 
    secret: "SECRET", 
    store: new MemoryStore(), 
    cookie: { 
     domain: 'example.com', 
     maxAge : 1000*60*60*24*30*12 
    } 
} 
app.use express.static "#{__dirname}/public" 
app.use express.logger "short" 
app.use express.favicon "#{__dirname}/public/img/favicon.ico" 
app.use app.router 

site.js:

module.exports = (app) -> 
app.get '/', (req, res) -> 
    console.log "/ hit with #{req.url} from #{req.headers.host}" 
    domains = req.headers.host.split "." 
    org = if domains then domains[0] else "www" 
    if org == "www" 
     res.render "index", { layout: null } 
    else 
     if req.session.user 
      console.log "session established" 
      res.render "app", { layout: null } 
     else 
      console.log "no session" 
      res.redirect "http://www.example.com/accounts/login"  

users.js:

users = require('../services/users') 
module.exports = (app) -> 
app.get "/accounts/login", (req, res) -> 
    res.render "login", { layout: null, locals: { returnUrl: req.query.returnUrl } } 
app.post "/accounts", (req, res) -> 
    users.authenticate app, req.body.login, req.body.password, (user) -> 
     if user 
      req.session.user = user 
      res.redirect "http://#{user.orgName}.example.com" 
     else 
      res.render "login", { layout: null, locals: { returnUrl: req.body.url } } 
app.get "/accounts/logout", (req, res) -> 
    console.log "deleting user from session" 
    delete req.session.user 
    res.redirect "http://www.example.com     

comprobarlo localmente en OSX, me han añadido www.example.com y site.example.com a mi archivo de hosts para que las búsquedas de DNS consiguen manejados localmente.

Respuesta

23

En primer lugar, para permitir que el navegador realice solicitudes entre dominios, debe establecer encabezados en el servidor. Esta solución funciona para solicitud normal y AJAX. En su función de configuración expreso:

Express 4.0:

var express = require('express'); 
var session = require('express-session'); 
var cookieParser = require('cookie-parser'); 

var app = express(); 

app.use(cookieParser()); 
app.use(session({ 
    secret: 'yoursecret', 
    cookie: { 
     path: '/', 
     domain: 'yourdomain.com', 
     maxAge: 1000 * 60 * 24 // 24 hours 
    } 
})); 
app.use(function(req, res, next) { 
    res.header('Access-Control-Allow-Credentials', true); 
    res.header('Access-Control-Allow-Origin', req.headers.origin); 
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); 
    res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept'); 
    next(); 
}); 

Access-Control-Allow-El origen puede estar en '*' si no hay intercambio de galletas entre dominios de sesiones necesarias. Para tener las cookies y la sesión compartida entre dominios, debe establecer Access-Control-Allow-Origin específico para el dominio en el que se realiza la solicitud, es por eso que req.headers.origin es perfecto para eso.

Usar el dominio no funcionará bien en localhost - así que asegúrese de deshabilitarlo en el entorno de desarrollo y habilitarlo en la producción. Permitirá cookies compartidas en los principales y subdominios.

Esto no es todo. Los navegadores en sí no enviarán cookies a través de solicitudes de dominios cruzados, y esto tiene que ser forzado. En jQuery puede agregar el parámetro adicional en $ .ajax() solicitud:

xhrFields: { withCredentials: true } 

Para no jQuery, sólo hay constructor XHR y ajustar este parámetro:

xhr.withCredentials = true; 

y ya está listo para hacerlo cruz -dominio con sesión compartida.

+1

user '$ .ajaxSetup' para agregar' withCredentials: true' a cada solicitud – Xerri

+0

@Maksims "Usar el dominio no funcionará bien en localhost" - ¿por qué no funciona en localhost? Tengo: api.localhost: 3000 y localhost: 3000 - ¿no funciona? : \ – AmpT

+0

Debido a la seguridad del navegador, no se enviará la cookie si el dominio actual no coincide con su dominio definido. Para entornos de desarrollo y escenario, puede comentar propiedades de dominio o establecerlas en "localhost". – moka

2

¿Se aseguró de tener las cookies configuradas en el dominio de nivel superior para que todos los subdominios puedan leerlas? Entonces solo es cuestión de persistir los datos de la sesión en la memoria, un DB, lo que sea como siempre. No tengo mi máquina de desarrollo en funcionamiento, pero será algo así en tu app.configure().

app.use(express.cookieParser()); 

app.use(express.session({ 
    key: 'A_SESSION_KEY', 
    secret: 'SOMETHING_REALLY_HARD_TO_GUESS', 
    store: new express.session.MemoryStore, 
    cookie: { 
    path  : '/', 
    domain : 'yourdomain.com', 
    httpOnly : true, 
    maxAge : 1000*60*60*24*30*12 //one year(ish) 
    } 
})); 
+1

He agregado en su sugerencia pero no ha hecho ninguna diferencia. Intenté 'example.com', '* .example.com' y '.example.com'. He agregado más código en mi pregunta original para detallar el proceso un poco mejor. Creo que puede ser un problema con la redirección ... – mattgi

+0

@mattgi Intenta eliminar todas las cookies de tu navegador después de cambiar el código. Esta respuesta me parece bien. – InspiredJW

+0

@InspiredJW lo he hecho ... ¿quizás es porque estoy ejecutando localmente? cuando establezco session.user = user y luego registro la sesión en la consola, puedo verla allí, pero en el momento en que redirijo el usuario desaparece. De hecho, puede no estar relacionado con el 'problema' del subdominio que estoy tratando de Resolver ya que parece que mi sesión simplemente no funciona en ninguna ruta (incluso en el mismo encabezado de host). – mattgi

0

Nota: Si se utiliza Express 4 y el nuevo módulo de galleta-sesión, el código es el

{ 
    secret: <session_secret> , 
    store: <session store> , 
    domain: '.domain.com', 
} 

Esto me mordió, pero la API ha cambiado.

Cuestiones relacionadas