2016-05-10 50 views
5

我有一些Express中間件處理來自我的客戶端應用程序的GET請求,以便向使用OAuth2令牌的單獨API服務器發出後續請求,我也在使用express-session用於存儲這些令牌。自定義req.session屬性值的更新似乎不夠快速持續

在我發出傳出請求的中間件中,我添加了處理以應對訪問令牌過期的情況(API服務器發回403)並請求刷新令牌,之後它將發出相同的令牌原始的傳出請求到API服務器,所以客戶端不知道這一切正在進行。然後通過express-session將檢索到的新令牌持久保存回會話存儲區,以供後續請求使用。令牌也用於設置授權承載令牌標頭,您將在下面看到。

這裏是我的特快代碼都涉及部分:

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(); 
    } 
    }); 
}; 

對於上述大部分工作就好

當用戶第一次登錄的,但一些額外的用戶個人資料信息是從API服務器獲取他們被帶到應用的主要頁面之前。

應用程序中的某些頁面也在頁面加載時獲取數據,因此受訪問令牌檢查的影響。

在正常使用期間,所以當用戶登錄並開始點擊頁面時,我可以看到這些令牌已經被換出並通過express-session在會話存儲中保存,並在其過期時保存。根據我編寫的中間件,新訪問令牌正確地用於後續請求。

我現在有一個場景,我的中間件不起作用。

所以說我在加載頁面加載數據的頁面,可以說它是一個訂單頁面。如果我等待API服務器上配置的令牌到期時間已過,然後刷新瀏覽器,客戶端應用程序將首先請求用戶信息,然後成功請求頁面所需的訂單數據(使用AngularJS承諾)

在我快速的應用程序的用戶信息請求,從API服務器獲取403等的令牌獲得通過我上面的中間件刷新和req.session.accessToken,我可以在我的服務器應用程序通過控制檯記錄見得到更新。但是下一次對訂單數據的提取最終將使用先前設置的訪問令牌,並且這會導致來自API服務器的進一步未授權錯誤,因爲請求是使用無效令牌進行的。

如果我再次刷新瀏覽器,用戶信息和訂單都使用來自以前的中間件流程的正確更新的令牌來獲取。

所以我不確定這裏發生了什麼,我想知道是否這是一個時間問題,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爲我的會話存儲,我已嘗試使用默認的內存存儲,但存在相同的行爲。

回答

3

它聽起來像一個競爭條件客戶端,如果你正在執行2個請求(檢查auth - 然後獲取數據)是第二個(獲取數據)嵌套到第一個調用成功?或者你是同時線性調用兩者嗎?

我的想法是:

客戶端 - 將用戶信息請求(會話ID 1) - 服務器處理

客戶端 - 獲取訂單信息請求(會話ID 1) - 服務器處理

服務器 - 響應用戶信息 - 403 - 客戶端更新會話ID

服務器 - 響應訂單信息 - 403

真的是你想要什麼我S:

客戶端 - 發送用戶信息請求(會話1) - 服務器處理

服務器 - 獲取用戶信息請求(403) - 客戶端更新會話ID

客戶端 - 獲取順序信息請求(會話2 ) - 服務器處理

服務器 - respondes訂單信息 - 實際結果

+0

嗨安德魯,不,兩個通話不被從客戶端在同一時間做。我正在使用angularjs承諾,並且第二個客戶端端調用請求不會在用戶信息請求承諾解決之前完成。我已經添加了一些廣泛的服務器端日誌記錄,並且可以看到來自客戶端的傳入請求會一個接一個地進行處理,令牌之間刷新令牌。我希望這是一個簡單的問題,就像你所說的那樣!謝謝 – mindparse

+0

然後我也可以建議這個職位:http://stackoverflow.com/questions/13090177/updating-cookie-session-in-express-not-registering-with-browser - 設置滾動標誌爲true以強制會話更新每個請求(以確保其不緩存) –

+0

謝謝,但我已經'滾動:true'設置 – mindparse