这是我的设置:
- 我在nodejs中实现了一个公开api端点的http服务器。这通过nginx反向代理到api.domain.com
和ssl。这是配置:
1 server {
2 listen 80;
3 server_name api.domain.com;
4 access_log /var/log/nginx/api.access.log;
5 location / {
6 proxy_pass http://127.0.0.1:3000/;
7 }
8 }
9
10 server {
11 listen 443;
12 server_name api.domain.com;
13 access_log /var/log/nginx/api.access.log;
14 ssl on;
15 ssl_certificate /path/to/ssl/server.crt;
16 ssl_certificate_key /path/to/ssl/server.key;
17 location / {
18 proxy_pass https://127.0.0.1:3001/;
19 }
20 }
然后我让nginx在dashboard.domain.com
下提供一个静态上下文文件,用于使用来自api.domain.com
的api。这是设置:
1 server {
2 listen 80;
3 server_name dashboard.domain.com;
4 root /path/to/static/site;
5 }
我想使用CORS执行此操作,我确保静态站点上的js在所有请求中发送正确的Origin
标头。我实现了一个非常简单的登录机制。这是我在api端点上使用的coffeescript代码:
# server.coffee
app.configure ->
app.use middleware.setP3PHeader()
app.use express.bodyParser()
app.use express.cookieParser()
app.use express.session
secret: conf.session.secret
key: conf.session.key
cookie:
maxAge: conf.session.maxAge
app.use express.methodOverride()
app.use express.query()
app.use express.errorHandler()
# routes.coffee
app.options '*', shop.cors, shop.options
app.post '/login', shop.cors, shop.login
app.post '/logout', shop.cors, shop.logout
app.get '/current-user', shop.cors, shop.current
# shop.coffee
exports.options = (req, res) ->
res.send 200
exports.cors = (req, res, next) ->
allowed = ['http://dashboard.domain.com', 'http://localhost:3000']
origin = req.get 'Origin'
if origin? and origin in allowed
res.set 'Access-Control-Allow-Origin', origin
res.set 'Access-Control-Allow-Credentials', true
res.set 'Access-Control-Allow-Methods', 'GET,POST'
res.set 'Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'
next()
else
res.send 403, "Not allowed for #{origin}"
exports.login = (req, res) ->
unless req.body.email? and req.body.password?
res.send 400, "Request params not correct #{req.body}"
models.Shop.findOne()
.where('email').equals(req.body.email)
.where('password').equals(req.body.password)
.exec (err, shop) ->
if err? then return res.send 500, err.message
unless shop? then return res.send 401, "Not found for #{req.body}"
req.session.shopId = shop.id
res.send 200, shop.publish()
exports.logout = (req, res) ->
delete req.session.shopId
res.send 200
exports.current = (req, res) ->
unless req.session.shopId?
return res.send 401, "Not logged in!"
models.Shop.findById(req.session.shopId)
.exec (err, shop) ->
if err? then return send.res 500, err.message
unless shop? then return res.send 404, "No shop for #{req.session.shopId}"
res.send 200, shop.publish()
问题在于:
1.我首先打电话给/login
并与登录用户(req.session.shopId
)进行新会话
然后我打电话给/current-user
,但会议结束了! nodejs服务器收到的会话ID不同,因此会创建不同的会话
答案 0 :(得分:4)
看起来你有一个全局代理指令(proxy_pass
),但是你没有明确地处理头转发(其中,可能是会话令牌作为cookie存在),而且你需要考虑什么构成(共享)会话缓存密钥。
可以尝试这样的事情:
location / {
proxy_pass http://127.0.0.1:3000/;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_cache pcache;
proxy_cache_key "$scheme$host$request_method$request_uri";
}
此外,如果node.js服务器位于同一个框中,则不确定为什么要通过localhost(proxy_pass https://127.0.0.1:3001/
)上的ssl进行连接。您可以考虑通过执行以下操作来考虑使用重写指令公开仅限SSL的公共面:rewrite ^ https://api.domain.com$request_uri? permanent;
另请参阅:基本设置上的Node.js + Nginx - What now? [SO]和http://www.ruby-forum.com/topic/4408747,以便对来自nginx反向代理配置snafu的丢失(和更糟糕的!)会话进行讨论。
答案 1 :(得分:1)
如果您想拥有会话持久性,则需要确保正确配置了express-session
模块。 resave
和saveUninitialized
参数很重要。
来自docs:
<强>重新保存强>
强制将会话保存回会话存储区,即使是 在请求期间从未修改过会话。取决于您的商店 这可能是必要的,但它也可以创造一个竞争条件 客户端向您的服务器发出两个并行请求并进行更改 一个请求中的会话可能会在另一个请求时被覆盖 结束,即使它没有做出任何改变(这种行为也取决于什么 存储你正在使用)。
默认值为true,但不推荐使用默认值, 因为默认将来会改变。请研究一下 设置并选择适合您的用例的内容。通常情况下, 你会想要假的。
我怎么知道这对我的商店是否有必要?了解最好的方法 如果它实现了触摸方法,请检查您的商店。如果它 那么你可以安全地设置resave:false。如果它没有实现 触摸方法和您的商店设置存储的到期日期 会话,然后你可能需要resave:true。
<强> saveUninitialized 强>
强制进行&#34;未初始化的会话&#34;被保存到商店。一个 会话在新的但未修改时未初始化。选择 false对于实现登录会话,减少服务器非常有用 存储使用,或遵守之前需要许可的法律 设置一个cookie。选择false也有助于竞争条件 客户端在没有会话的情况下发出多个并行请求。
默认值为true,但不推荐使用默认值, 因为默认将来会改变。请研究一下 设置并选择适合您的用例的内容。
请注意,如果您将Session与PassportJS,Passport结合使用 将为会话添加一个空的Passport对象以供用户使用 经过身份验证,将被视为对其的修改 会话,导致它被保存。
我的一个应用程序有这个并且它正在工作:
app.use(session({
store: new RedisStore({
host: config.redis_instance_local_ip,
port: config.redis_instance_local_port
}),
secret: config.application_session_secret,
resave: false,
saveUninitialized: true
}));
此外,如果你想在nginx负载均衡的多个节点上拥有粘性会话(或会话持久性),你应该有商业版的nginx,nginx plus。见http://nginx.com/products/session-persistence/