表达 Session 在 CORS 调用后不持久
Express Session not persisting after CORS calls
TL;DR:
Can't succeed persisting session across multiple APIs call performed
between a Backbone App and Node.js Server with Express,
Express-Session and Express-Cors. Looks like session is reinitialized/lost
after every call.
长版:
我在 localhost:3000
上有一个客户端 Backbone/React/Flux 应用程序 运行 在 localhost:4242
上的 Node.js 服务器 运行 上执行以下调用]:
Http 调用
RESPONSE HEADERS
Content-Type application/json; charset=utf-8
Set-Cookie connect.sid=s%3AFeNYY5GQGvkyRvOym7DhysprePaQr7xP.BrxOPP56k9pDpxQPvwjDFaxkEYoHU%2FAEtNUIXGltqjI; Domain=http://localhost:3000; Path=/
Vary Origin
X-Powered-By Express
access-control-allow-credentials true
access-control-allow-orign http://localhost:3000
[...]
REQUEST HEADERS
Accept application/json, text/javascript, */*; q=0.01
Content-Type application/json; charset=utf-8
Cookie connect.sid=s%3AjP4iADZvRnDHbJcCE8H81Zy6TIehj9BJ.eDOTw44GcHqq8i2dslBGd43tXMQ22ENl31fRizdC8iA
Host localhost:4242
Origin http://localhost:3000
Referer http://localhost:3000/login
[...]
RESPONSE HEADERS
Content-Type application/json; charset=utf-8
Set-Cookie connect.sid=s%3ARxf91_vLMBqzB6xN-0QFIIk_-SyBP9_8.F1Mr%2BVSkYNJ6MqnzO%2BsxxfwXRinIX6th80SoukG1QBM;Domain=http://localhost:3000; Path=/
Vary Origin
X-Powered-By Express
access-control-allow-credentials true
access-control-allow-orign http://localhost:3000
[...]
REQUEST HEADERS
Accept application/json, text/javascript, */*; q=0.01
Content-Type application/json; charset=utf-8
Cookie connect.sid=s%3AjP4iADZvRnDHbJcCE8H81Zy6TIehj9BJ.eDOTw44GcHqq8i2dslBGd43tXMQ22ENl31fRizdC8iA
Host localhost:4242
Origin http://localhost:3000
Referer http://localhost:3000/login
[...]
基本上第一个调用 POST /api/session
是 logging-in 用户并试图在 session 中存储一个 API 令牌。
第二次调用 GET /api/users
在第一次调用成功后立即触发,并检索用户信息。
Backbone方法
这是我在 Session 模型上针对 logging-in 的 Backbone 方法:
login: (options) ->
@set {user: options.user, password: options.password}
@save ['user', 'password'],
success: (data) =>
@set({authenticated: true, accessToken: data.accessToken, password: null})
options.success(data) # trigger the second call here
error: (error) =>
options.error(error)
并在我的 UserStore
中调用 /api/users
users: (options) ->
@users.fetch
success: (users) =>
@users = users
options.success(users)
使用这些不同的选项(我在 Backbone.Collection/Backbone.Model 中覆盖了 Backbone.sync):
class UsersCollection extends Backbone.Collection
url: '/api/users'
model: UserModel
sync: (method, model, options) ->
options ?= {}
options.url ?= @url
options.dataType ?= 'json'
options.contentType ?= "application/json; charset=utf-8"
options.crossDomain ?= true
options.xhrFields ?= {"withCredentials": true}
super(method, model, options)
(简化版:模型和 Collection 都一样,使用 BaseCollection 和 BaseModel,我在这里重写了 sync() 方法)。
因此 Backbone.sync(method, model, options)
中的 Console.log(options)
返回:
{"url":"http://localhost:4242/api/session","dataType":"json","contentType":"application/json; charset=utf-8","crossDomain":true,"validate":true,"parse":true,"xhrFields":{"withCredentials":true}}
Node.js 设置和方法
这是我的 Node.js 路由器 setting-up Express:
BodyParser = require 'body-parser'
Session = require 'express-session'
Cors = require 'cors'
class Router
constructor: (express) ->
@express = express
@express.use BodyParser.json()
@express.use Cors(@corsConfig())
@express.use Session(@sessionConfig())
# Express routes are set here
# @express.post '/api/session', (request, response) => [...]
# @express.get '/api/users', (request, response) => [...]
corsConfig: ->
origin: 'http://localhost:3000'
credentials: true
sessionConfig: ->
secret: 'whatever'
cookie:
secure: false
httpOnly: false
domain: 'http://localhost:3000'
这是我的 Node.js 方法处理 POST /api/session
login: (request, response) ->
session = request.session
console.log JSON.stringify(session)
console.log request.sessionID
console.log '---------------------------------------------'
if session.accessToken
console.log 'session with token!'
response.json {accessToken: session.accessToken}
else
console.log 'performing credentialAuthentication'
user = request.body.user
password = request.body.password
@whatever.authentication
user: user
password: password
success: (accessToken) ->
request.session.accessToken = accessToken
console.log JSON.stringify(session)
console.log request.sessionID
console.log '---------------------------------------------!!!'
response.json {accessToken: accessToken}
# also tried with response.send()
和处理 GET /api/users
@express.get '/api/users', (request, response) =>
console.log JSON.stringify(request.session)
console.log request.sessionID
console.log '---------------------------------------------'
[...]
Node.js日志
这是日志:
express:router dispatching OPTIONS /api/session
express:router dispatching POST /api/session
{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":false,"path":"/"}}
zse18d2zrNRdEXPjFHF0gm3NkONb-_5V
---------------------------------------------
performing credentialAuthentication
{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":false,"path":"/"},
"accessToken":"ebab5010f9ece5ea984e4b73f9a46ef3"}
zse18d2zrNRdEXPjFHF0gm3NkONb-_5V
---------------------------------------------!!!
express:router dispatching GET /api/users
{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":false,"path":"/"}}
g8YXQEpt_rnWSGdh1nCKMndiI8Lt2UDq
---------------------------------------------
如您所见,CORS 请求正常执行,我正在正确获取令牌,然后尝试将其存储在 session 中。
但是在第二次调用中,session 没有保留,我无法访问我在第一次调用中实际设置的变量 (accessToken)。
查看日志和两次调用的 HTTP Headers,看起来 session 每次都重新初始化,因为 session ID 正在更改并且每个请求- 每次都会发送 Set-Request header (据我所知,情况不应该如此)。
我怀疑此行为是由 CORS 级别的一些不一致或缺失的配置引起的,或者是由于为 Session 设置的 path
(Express.use(path, middleware)
和 Cookie({path: '/'})
).然而,尽管尝试了很多不同的配置、设置和 headers,但我真的无法让它工作。
非常欢迎任何能够就此行为以及我所缺少的内容向我提供启发的人:)
谢谢!
PS:我向 non-CoffeeScript 开发人员致歉;)
这是答案(简单的):
我错误地配置了我的 Session 中间件; cookie.domain
选项导致了问题。
这是正确的配置:
sessionConfig: ->
secret: 'whatever'
cookie:
secure: false
httpOnly: false
这个选项不是 required/existing(现在?)与 express/session 中间件;不确定为什么我最初使用它,可能来自过时的参考(express/cookie-parser 或 express/cors)。
TL;DR:
Can't succeed persisting session across multiple APIs call performed between a Backbone App and Node.js Server with Express, Express-Session and Express-Cors. Looks like session is reinitialized/lost after every call.
长版:
我在 localhost:3000
上有一个客户端 Backbone/React/Flux 应用程序 运行 在 localhost:4242
上的 Node.js 服务器 运行 上执行以下调用]:
Http 调用
RESPONSE HEADERS
Content-Type application/json; charset=utf-8
Set-Cookie connect.sid=s%3AFeNYY5GQGvkyRvOym7DhysprePaQr7xP.BrxOPP56k9pDpxQPvwjDFaxkEYoHU%2FAEtNUIXGltqjI; Domain=http://localhost:3000; Path=/
Vary Origin
X-Powered-By Express
access-control-allow-credentials true
access-control-allow-orign http://localhost:3000
[...]
REQUEST HEADERS
Accept application/json, text/javascript, */*; q=0.01
Content-Type application/json; charset=utf-8
Cookie connect.sid=s%3AjP4iADZvRnDHbJcCE8H81Zy6TIehj9BJ.eDOTw44GcHqq8i2dslBGd43tXMQ22ENl31fRizdC8iA
Host localhost:4242
Origin http://localhost:3000
Referer http://localhost:3000/login
[...]
RESPONSE HEADERS
Content-Type application/json; charset=utf-8
Set-Cookie connect.sid=s%3ARxf91_vLMBqzB6xN-0QFIIk_-SyBP9_8.F1Mr%2BVSkYNJ6MqnzO%2BsxxfwXRinIX6th80SoukG1QBM;Domain=http://localhost:3000; Path=/
Vary Origin
X-Powered-By Express
access-control-allow-credentials true
access-control-allow-orign http://localhost:3000
[...]
REQUEST HEADERS
Accept application/json, text/javascript, */*; q=0.01
Content-Type application/json; charset=utf-8
Cookie connect.sid=s%3AjP4iADZvRnDHbJcCE8H81Zy6TIehj9BJ.eDOTw44GcHqq8i2dslBGd43tXMQ22ENl31fRizdC8iA
Host localhost:4242
Origin http://localhost:3000
Referer http://localhost:3000/login
[...]
基本上第一个调用 POST /api/session
是 logging-in 用户并试图在 session 中存储一个 API 令牌。
第二次调用 GET /api/users
在第一次调用成功后立即触发,并检索用户信息。
Backbone方法
这是我在 Session 模型上针对 logging-in 的 Backbone 方法:
login: (options) ->
@set {user: options.user, password: options.password}
@save ['user', 'password'],
success: (data) =>
@set({authenticated: true, accessToken: data.accessToken, password: null})
options.success(data) # trigger the second call here
error: (error) =>
options.error(error)
并在我的 UserStore
中调用 /api/usersusers: (options) ->
@users.fetch
success: (users) =>
@users = users
options.success(users)
使用这些不同的选项(我在 Backbone.Collection/Backbone.Model 中覆盖了 Backbone.sync):
class UsersCollection extends Backbone.Collection
url: '/api/users'
model: UserModel
sync: (method, model, options) ->
options ?= {}
options.url ?= @url
options.dataType ?= 'json'
options.contentType ?= "application/json; charset=utf-8"
options.crossDomain ?= true
options.xhrFields ?= {"withCredentials": true}
super(method, model, options)
(简化版:模型和 Collection 都一样,使用 BaseCollection 和 BaseModel,我在这里重写了 sync() 方法)。
因此 Backbone.sync(method, model, options)
中的 Console.log(options)
返回:
{"url":"http://localhost:4242/api/session","dataType":"json","contentType":"application/json; charset=utf-8","crossDomain":true,"validate":true,"parse":true,"xhrFields":{"withCredentials":true}}
Node.js 设置和方法
这是我的 Node.js 路由器 setting-up Express:
BodyParser = require 'body-parser'
Session = require 'express-session'
Cors = require 'cors'
class Router
constructor: (express) ->
@express = express
@express.use BodyParser.json()
@express.use Cors(@corsConfig())
@express.use Session(@sessionConfig())
# Express routes are set here
# @express.post '/api/session', (request, response) => [...]
# @express.get '/api/users', (request, response) => [...]
corsConfig: ->
origin: 'http://localhost:3000'
credentials: true
sessionConfig: ->
secret: 'whatever'
cookie:
secure: false
httpOnly: false
domain: 'http://localhost:3000'
这是我的 Node.js 方法处理 POST /api/session
login: (request, response) ->
session = request.session
console.log JSON.stringify(session)
console.log request.sessionID
console.log '---------------------------------------------'
if session.accessToken
console.log 'session with token!'
response.json {accessToken: session.accessToken}
else
console.log 'performing credentialAuthentication'
user = request.body.user
password = request.body.password
@whatever.authentication
user: user
password: password
success: (accessToken) ->
request.session.accessToken = accessToken
console.log JSON.stringify(session)
console.log request.sessionID
console.log '---------------------------------------------!!!'
response.json {accessToken: accessToken}
# also tried with response.send()
和处理 GET /api/users
@express.get '/api/users', (request, response) =>
console.log JSON.stringify(request.session)
console.log request.sessionID
console.log '---------------------------------------------'
[...]
Node.js日志
这是日志:
express:router dispatching OPTIONS /api/session
express:router dispatching POST /api/session
{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":false,"path":"/"}}
zse18d2zrNRdEXPjFHF0gm3NkONb-_5V
---------------------------------------------
performing credentialAuthentication
{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":false,"path":"/"},
"accessToken":"ebab5010f9ece5ea984e4b73f9a46ef3"}
zse18d2zrNRdEXPjFHF0gm3NkONb-_5V
---------------------------------------------!!!
express:router dispatching GET /api/users
{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":false,"path":"/"}}
g8YXQEpt_rnWSGdh1nCKMndiI8Lt2UDq
---------------------------------------------
如您所见,CORS 请求正常执行,我正在正确获取令牌,然后尝试将其存储在 session 中。
但是在第二次调用中,session 没有保留,我无法访问我在第一次调用中实际设置的变量 (accessToken)。
查看日志和两次调用的 HTTP Headers,看起来 session 每次都重新初始化,因为 session ID 正在更改并且每个请求- 每次都会发送 Set-Request header (据我所知,情况不应该如此)。
我怀疑此行为是由 CORS 级别的一些不一致或缺失的配置引起的,或者是由于为 Session 设置的 path
(Express.use(path, middleware)
和 Cookie({path: '/'})
).然而,尽管尝试了很多不同的配置、设置和 headers,但我真的无法让它工作。
非常欢迎任何能够就此行为以及我所缺少的内容向我提供启发的人:)
谢谢!
PS:我向 non-CoffeeScript 开发人员致歉;)
这是答案(简单的):
我错误地配置了我的 Session 中间件; cookie.domain
选项导致了问题。
这是正确的配置:
sessionConfig: ->
secret: 'whatever'
cookie:
secure: false
httpOnly: false
这个选项不是 required/existing(现在?)与 express/session 中间件;不确定为什么我最初使用它,可能来自过时的参考(express/cookie-parser 或 express/cors)。