node.js 使用 Express 和 Node,如何跨子域/主机头维护会话
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9071969/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using Express and Node, how to maintain a Session across subdomains/hostheaders
提问by mattgi
I have a single node server that responds to requests and redirects a user based on host headers. The usage is that the static/home site lives at www and each user has their own sub domain (i.e. www.example.com and site.example.com). The routing is as per site.js.
我有一个单节点服务器,它响应请求并根据主机标头重定向用户。用法是静态/home 站点位于www 并且每个用户都有自己的子域(即www.example.com 和site.example.com)。路由是根据site.js。
When the user is not logged in they are redirected to login.
当用户未登录时,他们将被重定向到登录。
I am discovering that the session is not maintained when the user is redirected to their sub domain. I guess this is expected, but I am wondering if there is a way to maintain the same session across both sub domains.
我发现当用户被重定向到他们的子域时,会话没有被维护。我想这是意料之中的,但我想知道是否有办法在两个子域之间保持相同的会话。
I was hoping that if they were logged in and returned to www.example.com they would see a different view that included a link to logout / their dashboard, etc. My workaround at the moment, I'm thinking, is to just create the session on their subdomain and if they do return to www it will just be as if they are not logged in.
我希望如果他们登录并返回 www.example.com,他们会看到一个不同的视图,其中包括一个注销链接/他们的仪表板等。我现在的解决方法是,我想,只是创建他们子域上的会话,如果他们确实返回了 www,那就像他们没有登录一样。
Anyone dealt with this before or have answers on how to handle sessions in this manner?
任何人之前处理过这个问题,或者有关于如何以这种方式处理会话的答案?
I think the issue may be in users.js where I redirect to 'http://site.example.com' as its not a relative path...
我认为问题可能出在我重定向到“http://site.example.com”的 users.js 中,因为它不是相对路径...
Here is the relevant code (the user lookup is done using MongoDB and I've left it out as its working fine - the line that calls this service is users.authenticate)...
这是相关的代码(用户查找是使用 MongoDB 完成的,我将其排除在外,因为它工作正常——调用此服务的行是 users.authenticate)...
server.js:
服务器.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:
网站.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:
用户.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
To test it locally on OSX, I have added www.example.com and site.example.com in to my hosts file so that the DNS lookups get handled locally.
为了在 OSX 上进行本地测试,我将 www.example.com 和 site.example.com 添加到我的主机文件中,以便在本地处理 DNS 查找。
回答by moka
First of all to allow browser to make cross-domain requests you need to set headers on server side. This solution works for normal request as well as AJAX. In your express configure function:
首先,要允许浏览器进行跨域请求,您需要在服务器端设置标头。此解决方案适用于普通请求以及 AJAX。在您的快速配置功能中:
Express 4.0:
快递 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-Origin can be set to '*' if no cross-domain cookies exchange for sessions needed. To have cookies and session shared cross-domain you need to set specific Access-Control-Allow-Origin to actually domain where request is made from, that's why req.headers.origin - is perfect for that.
如果不需要跨域 cookie 交换会话,则 Access-Control-Allow-Origin 可以设置为“*”。要跨域共享 cookie 和会话,您需要将特定的 Access-Control-Allow-Origin 设置为发出请求的实际域,这就是为什么 req.headers.origin - 是完美的。
Using domain it wont work well on localhost - so make sure you disable it in development environment, and enable on production. It will enable shared cookies across top and sub domains.
使用域在 localhost 上无法正常工作 - 因此请确保在开发环境中禁用它,并在生产中启用。它将启用跨顶级域和子域的共享 cookie。
This is not all. Browsers it self won't send cookies over cross domain requests, and this have to be forced. In jQuery you can add extra parameter in $.ajax() request:
这还不是全部。浏览器本身不会通过跨域请求发送 cookie,这是必须的。在 jQuery 中,您可以在 $.ajax() 请求中添加额外的参数:
xhrFields: { withCredentials: true }
For non jQuery, just have XHR constructor and set this parameter:
对于非 jQuery,只需使用 XHR 构造函数并设置此参数:
xhr.withCredentials = true;
And you are ready to do cross-domain with shared session.
您已准备好使用共享会话进行跨域。
回答by daviddripps
Did you make sure you have your cookies set to the top-level domain so it can be read by all subdomains? Then it's just a matter or persisting your session data in memory, a db, whatever as usual. I don't have my dev machine up and running, but it'll be something like this in your app.configure().
您是否确保将 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)
}
}));
回答by Kevin Boyle
Note: If using Express 4 and the new cookie-session module, the code looks like
注意:如果使用 Express 4 和新的 cookie-session 模块,代码看起来像
{
secret: <session_secret> ,
store: <session store> ,
domain: '.domain.com',
}
This bit me, but the API has changed.
这有点我,但 API 已经改变。

