在CORS呼叫

时间:2015-11-27 13:53:59

标签: node.js backbone.js express cors express-session

TL; DR:

  

无法成功执行跨多个API调用的会话   在Backbone App和带有Express的Node.js服务器之间,   Express-Session和Express-Cors。看起来会话重新初始化/丢失   每次通话后。

长版:

我正在localhost:3000上运行客户端Backbone / React / Flux应用程序,在localhost:4242上运行的Node.js服务器上执行以下调用:

Http调用

  

POST http://localhost:4242/api/session

 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
 [...]
  

获取http://localhost:4242/api/users

 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在第一个调用成功后立即触发,并检索用户信息。

骨干方法

这是我登录的Session模型的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)

(简化版:同样适用于Models和Collection,使用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'

这是我处理POST /api/session

的Node.js方法
  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标头,看起来会话每次重新初始化,因为会话ID正在更改并且每个请求 - 并且每次都发送一个Set-Request标头(不应该是这样的,据我所知)。

我怀疑这种行为是由CORS级别的一些不连贯或缺失的配置引起的,或者是由于pathExpress.use(path, middleware)的{​​{1}}设置引起的。然而,尽管许多尝试使用不同的配置,设置和标题,我真的无法使其工作。

任何能够启发我这种行为以及我所缺少的人都非常欢迎:) 谢谢!

PS:我向非CoffeeScript开发人员道歉;)

1 个答案:

答案 0 :(得分:4)

这是答案(简单的答案): 我错误配置了我的Session中间件; cookie.domain选项导致了问题。 这是正确的配置:

sessionConfig: ->
  secret: 'whatever'
  cookie:
    secure: false
    httpOnly: false

这个选项对于快速/会话中间件不是必需的/现有的(不再是?);不知道为什么我最初使用它,可能来自过时的引用(表达/ cookie-parser或express / cors)。