2012-01-30 26 views
11

我有一個節點服務器,它響應請求並根據主機頭重定向用戶。用法是靜態/主頁網站位於www,每個用戶都有自己的子域(即www.example.com和site.example.com)。路由是按照site.js。使用Express和Node,如何在子域/主機頭之間維護會話

當用戶沒有登錄時,他們被重定向到登錄。

我發現當用戶被重定向到他們的子域時,會話不會被維護。我想這是預期的,但我想知道是否有辦法在兩個子域中維護同一個會話。

我一直希望,如果他們登錄並返回到www.example.com,他們會看到一個不同的視圖,其中包括一個鏈接註銷/他們的儀表板等。我現在的解決方法,我想,只是在他們的子域創建會話,如果他們確實返回www它就好像他們沒有登錄。

任何人之前處理過或有如何處理這種方式的會話的答案?

我認爲這個問題可能是users.js,我重定向到「http://site.example.com」作爲它不是一個相對路徑...

下面是相關代碼(用戶查找使用MongoDB的做,我已經離開它作爲其做工精細 - 調用該服務是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     

本地測試它在OSX,我已經加入www.example.com和site.example.com在我的hosts文件,使DNS查找得到本地處理。

回答

23

首先爲了讓瀏覽器發出跨域請求,你需要在服務器端設置標題。該解決方案適用於正常請求以及AJAX。 在您的明確配置功能:

快車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(); 
}); 

訪問控制允許來源如果需要也可以不爲會議跨域餅乾交易所設定爲「*」。 要讓cookie和會話共享跨域,您需要將特定的Access-Control-Allow-Origin設置爲實際的請求所在的域,這就是爲什麼req.headers.origin - 非常適合這一點。

使用域它不會在localhost上很好地工作 - 所以確保你在開發環境中禁用它,並啓用生產。它將啓用頂級域和子域之間的共享cookie。

這不是全部。 它自己的瀏覽器不會通過跨域請求發送cookie,這必須被強制。 在jQuery中,你可以在$就添加額外的參數()請求:

xhrFields: { withCredentials: true } 

對於非jQuery的,只是有XHR構造函數,並設置該參數:

xhr.withCredentials = true; 

,你準備做交叉與共享會話的域。

+1

用戶'$ .ajaxSetup'爲每個請求添加'withCredentials:true' – Xerri 2013-07-15 08:09:48

+0

@Maksims「使用域名在本地主機上不會很好地工作」 - 爲什麼它不能在localhost上運行?我有:api.localhost:3000和localhost:3000 - 不工作? :\ – AmpT 2014-02-27 13:01:21

+0

由於瀏覽器的安全性,如果當前域與其定義的域不匹配,cookie將不會被髮送。對於開發和舞臺環境,您可以註釋掉域屬性,或將其設置爲「本地主機」。 – moka 2014-02-28 01:07:34

2

您是否確定自己的Cookie設置爲頂級域名,以便它可以被所有子域名讀取?然後,這只是一個問題或堅持你的會話數據在內存中,一個數據庫,無論如何。我沒有開發我的開發機器,但它在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

我已經在您的建議中添加了,但沒有任何區別。我嘗試了'example.com','* .example.com'和'.example.com'。我在原始問題中添加了更多代碼,以便更好地詳細說明該過程。我認爲這可能是重定向的問題... – mattgi 2012-01-31 22:35:28

+0

@mattgi嘗試在更改代碼後刪除瀏覽器中的所有Cookie。這個答案對我來說似乎很好。 – InspiredJW 2012-02-01 15:11:17

+0

@InspiredJW已經做到了..也許是因爲我在本地運行?當我設置session.user = user然後將會話記錄到控制檯我可以在那裏看到它,但是當我重定向用戶消失的時候..它可能實際上與子域「問題」無關我試圖因爲看起來我的會話根本不在任何路由中工作(即使在同一主機頭上)。 – mattgi 2012-02-02 02:01:13

0

注意:如果使用Express 4的和新的Cookie的會話模塊,代碼看起來像

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

這咬了我,但是API已經改變。

相關問題