首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >自定义req.session属性值的更新似乎不够快。

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

Stack Overflow用户
提问于 2016-05-10 08:06:31
回答 1查看 599关注 0票数 5

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

在发出传出请求的中间件中,我添加了处理,以处理访问令牌过期的情况(API服务器发送回403)并请求刷新令牌,然后它将向API服务器发出相同的原始传出请求,因此客户端不知道这一切正在发生。然后通过express-session将检索到的新令牌持久化回会话存储,以便在随后的请求中使用。这些令牌还用于设置授权承载令牌头,如下所示。

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

routes.controller.js

代码语言:javascript
运行
复制
//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

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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();
    }
  });
};

在大多数情况下,上面的内容工作得很好。

当用户第一次登录时,将从API服务器获取一些额外的用户配置文件信息,然后将其带到应用程序的主页。

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

在正常使用期间,当用户登录并开始在页面周围单击时,我可以看到令牌在过期时通过express-session被交换并保存在会话存储中。按照我编写的中间件,新的访问令牌被正确地用于后续请求。

我现在有了一个我的中间件不能工作的场景。

因此,假设我在一个页面上加载数据,让我们说它是一个订单页面。如果我等到API服务器上配置的令牌过期时间通过,然后刷新浏览器,客户端应用程序将首先请求用户信息,成功后将请求页面所需的订单数据(使用AngularJS承诺)。

在我的Express应用程序中,用户信息请求从API服务器获得403,因此通过上面的中间件刷新令牌,并更新req.session.accessToken,我可以通过控制台在服务器应用程序中看到这些更新。但是,下一次获取订单的数据将使用先前设置的访问令牌,这将导致API服务器进一步出现未经授权的错误,因为正在使用无效令牌发出请求。

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

所以我不知道这里发生了什么,我想知道这是否是一个时间问题,因为req.session对象没有被及时地保存回会话存储区,以便下一个请求得到响应?

有人知道这是怎么回事吗?

谢谢

更新1

正如注释中所要求的那样,下面是正在发出的两个请求的请求和响应头。

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

请求头

代码语言:javascript
运行
复制
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

响应头

代码语言:javascript
运行
复制
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

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

请求头

代码语言:javascript
运行
复制
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

响应头

代码语言:javascript
运行
复制
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作为会话存储,我尝试过使用默认内存存储,但也存在相同的行为。

EN

回答 1

Stack Overflow用户

发布于 2016-05-12 08:14:28

这听起来像是一个竞争条件客户端,如果您正在执行2个请求(检查auth -然后获取数据),第二个(获取数据)是否嵌套在第一个调用成功中?还是你是在线性地同时打电话?

我的想法是:

客户端-发送用户信息请求(sessionid 1) -服务器处理

客户端-获取订单信息请求(sessionid 1) -服务器处理

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

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

你真正想要的是:

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

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

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

服务器-响应顺序信息-实际结果

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37132634

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档