更新自定义req.session属性值似乎不够快速

我有一些Express中间件处理来自我的客户端应用程序的GET请求,以便向使用OAuth2令牌的单独的API服务器发出后续请求,我也使用express-session存储这些令牌。

在我的中间件,使传出的请求,我已经添加处理,以应付访问令牌到期的场合(API服务器发回一个403),并提出刷新令牌的请求,然后它将发出相同的原始传出请求API服务器,所以客户端不知道这一切正在进行。 然后,通过express-session会话将检索到的新令牌持久保存回会话存储express-session ,以便在随后的请求中使用。 令牌也用于设置授权载体令牌标题,您将在下面看到。

以下是我的Express代码中涉及的部分:

routes.controller.js

 //Currently handling GET API requests from client module.exports.fetch = function(req, res) { var options = helpers.buildAPIRequestOptions(req); helpers.performOutgoingRequest(req, res, options); }; 

helpers.js

 module.exports.buildAPIRequestOptions = function(req, url) { var options = {}; options.method = req.method; options.uri = 'http://someurl.com' + req.path; options.qs = req.query; options.headers = { 'Authorization': 'Bearer ' + req.session.accessToken }; return options; }; module.exports.performOutgoingRequest = function(req, res, options) { request(options, function(err, response, body){ if(response.statusCode === 401){ console.log(chalk.red('\n--- 401 RESPONSE RECEIVED TRY REFRESHING TOKENS ---')); //Note the third param to call below is a callback and is invoked when calling next() in the refreshToken middleware authController.refreshToken(req, res, function(){ console.log(chalk.green('\n--- RETRYING ORIGINAL REQUEST WITH UPDATED ACCESS TOKEN ---')); //Re-use original request options, but making sure we update the Authorization header beforehand options.headers.Authorization = 'Bearer ' + req.session.accessToken; retryOutgoingRequest(res, options); }); } else { res.status(response.statusCode).send(body); } }); }; function retryOutgoingRequest(res, options) { request(options, function(err, response, body){ if(err) { console.log(err); } res.status(response.statusCode).send(body); }); }; 

auth.controller.js

 module.exports.refreshToken = function(req, res, next) { var formData = { grant_type: 'refresh_token', refresh_token: req.session.refreshToken }, headers = { 'Authorization' : 'Basic ' + consts.CLIENT_KEY_SECRET_BASE64 }; request.post({url:consts.ACCESS_TOKEN_REQUEST_URL, form:formData, headers: headers, rejectUnauthorized: false}, function(err, response, body){ var responseBody = JSON.parse(body); if (response.statusCode === 200) { req.session.accessToken = responseBody.access_token; req.session.refreshToken = responseBody.refresh_token; next(); } else { console.log(chalk.yellow('A problem occurred refreshing tokens, sending 401 HTTP response back to client...')); res.status(401).send(); } }); }; 

以上大部分工作正常

当用户第一次login时,一些额外的用户configuration文件信息在被带到应用程序的主页面之前从API服务器获取。

应用程序中的某些页面也在页面加载时获取数据,因此受到访问令牌检查的影响。

在正常使用期间,所以当用户login并开始点击页面时,我可以看到令牌被换出并通过express-session保存在会话存储中,并在其到期时保存。 根据我写的中间件,新的访问令牌被正确地用于后续请求。

我现在有一个场景,我的中间件不起作用。

所以说,我在加载页面上的数据页面,可以说它的订单页面。 如果我等待API服务器上configuration的令牌到期时间已过,然后刷新浏览器,则客户端应用程序将首先请求用户信息,然后成功请求页面所需的订单数据(使用AngularJS承诺)

在我的快速应用程序中,用户信息请求从API服务器获得403,所以令牌通过上面的中间件刷新,并且req.session.accessToken得到更新,我可以通过控制台在我的服务器应用程序中logging日志。 但是下一次获取订单数据的结果是使用先前设置的访问令牌,并且由于使用无效令牌进行请求,这会导致来自API服务器的进一步的未经授权的错误。

如果我再次刷新浏览器,用户信息和订单都会使用来自以前的中间件stream程的正确更新的令牌来获取。

所以我不确定这里发生了什么事情,我想知道是否req.session对象没有被持久地保存到会话存储中,以便下一个请求可以接收到时间问题?

任何人有什么想法可能发生在这里?

谢谢

更新1

根据评论的要求,这里是两个请求的请求和响应头。

第一个请求(使用更新的令牌服务器端)

请求头

 GET /api/userinfo HTTP/1.1 Host: localhost:5000 Connection: keep-alive Accept: application/json, text/plain, */* User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36 Referer: https://localhost:5000/ Accept-Encoding: gzip, deflate, sdch Accept-Language: en-GB,en-US;q=0.8,en;q=0.6 Cookie: interact.sid=s%3A0NDG_bn67NeGQAYl1wP1-TmM19ExavFm.Zjv65e9BtSyNBuo%2FDxZEk2Np0963frVur4zHyYw3y5I 

响应头

 HTTP/1.1 200 OK X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Strict-Transport-Security: max-age=86400 X-Download-Options: noopen X-XSS-Protection: 1; mode=block Content-Type: text/html; charset=utf-8 Content-Length: 364 ETag: W/"16c-4AIbpZmTm3I+Yl+SbZdirw" set-cookie: interact.sid=s%3A0NDG_bn67NeGQAYl1wP1-TmM19ExavFm.Zjv65e9BtSyNBuo%2FDxZEk2Np0963frVur4zHyYw3y5I; Path=/; Expires=Fri, 13 May 2016 11:54:56 GMT; HttpOnly; Secure Date: Fri, 13 May 2016 11:24:56 GMT Connection: keep-alive 

第二个请求(使用旧的令牌服务器端)

请求头

 GET /api/customers HTTP/1.1 Host: localhost:5000 Connection: keep-alive Accept: application/json, text/plain, */* User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36 Referer: https://localhost:5000/ Accept-Encoding: gzip, deflate, sdch Accept-Language: en-GB,en-US;q=0.8,en;q=0.6 Cookie: interact.sid=s%3A0NDG_bn67NeGQAYl1wP1-TmM19ExavFm.Zjv65e9BtSyNBuo%2FDxZEk2Np0963frVur4zHyYw3y5I 

响应头

 HTTP/1.1 401 Unauthorized X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Strict-Transport-Security: max-age=86400 X-Download-Options: noopen X-XSS-Protection: 1; mode=block set-cookie: interact.sid=s%3A0NDG_bn67NeGQAYl1wP1-TmM19ExavFm.Zjv65e9BtSyNBuo%2FDxZEk2Np0963frVur4zHyYw3y5I; Path=/; Expires=Fri, 13 May 2016 11:54:56 GMT; HttpOnly; Secure Date: Fri, 13 May 2016 11:24:56 GMT Connection: keep-alive Content-Length: 0 

更新2

我还应该提到我正在使用connect-mongo作为会话存储,我尝试使用默认的内存存储,但存在相同的行为。

这听起来像是一个竞争条件客户端,如果你正在执行2个请求(检查auth – 然后获取数据)是第二个(获取数据)嵌套到第一个调用成功? 或者你正在线性同时调用两个?

我的想法是:

客户端 – 发送用户信息请求(会话ID 1) – 服务器处理

客户端 – 获取订单信息请求(会话ID 1) – 服务器处理

服务器 – 响应用户信息 – 403 – 客户端更新会话ID

服务器 – 响应订单信息 – 403

真正你想要的是:

客户端 – 发送用户信息请求(会话1) – 服务器处理

服务器 – 获取用户信息请求(403) – 客户端更新会话ID

客户端 – 获取订单信息请求(会话2) – 服务器处理

服务器 – 响应订单信息 – 实际结果