TL; ДР:
Не удается выполнить длительный сеанс при выполнении нескольких вызовов API. между Backbone App и Node.js Server с Express, Express-Session и Express-Cors. Похоже, сеанс повторно инициализирован/потерян после каждого вызова.
Длинная версия:
У меня есть приложение Backbone/React/Flux для клиента, работающее на localhost:3000
, выполняющее следующий вызов на сервере Node.js, работающем на localhost:4242
:
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
регистрирует пользователя и пытается сохранить токен API в сеансе.
Второй вызов GET /api/users
запускается сразу после первого успешного завершения и получения информации о пользователе.
Магистральный метод
Здесь мой метод Backbone в модели Session для входа в систему:
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)
И вызов /api/users в моем UserStore
users: (options) ->
@users.fetch
success: (users) =>
@users = users
options.success(users)
Используя эти различные параметры (я переопределил Backbone.sync в Backbone.Collection/Backbone.Model):
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)
(Упрощенная версия: то же самое для обеих моделей и коллекции, используя BaseCollection и BaseModel, где я переопределяю метод sync()).
Так что Console.log(options)
в Backbone.sync(method, model, 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 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 работает нормально, я правильно получаю свой токен, а затем пытаюсь сохранить его в сеансе.
Однако во втором вызове сеанс не сохраняется и я не могу получить доступ к переменной (accessToken), которую я фактически устанавливаю при первом вызове.
Глядя на журнал и на заголовки HTTP двух вызовов, похоже, что сеанс повторно инициализируется каждый раз, когда идентификатор сеанса меняется и каждый запрос - и каждый раз заголовок Set-Request отправляется (что насколько мне известно, не должно быть так.
Я подозреваю, что это поведение вызвано некоторой некогерентной или отсутствующей конфигурацией на уровне CORS или из-за установки path
для сеансов (Express.use(path, middleware)
и Cookie({path: '/'})
). Однако, несмотря на многие попытки с другой конфигурацией, настройкой и заголовками, я действительно не могу заставить его работать.
Любой, кто может просветить меня по этому поведению, и то, что мне не хватает, очень приветствуется:)
Спасибо!
PS: Приношу свои извинения разработчикам non-CoffeeScript;)