我有一个现有的单页面应用程序,我已转换为JWT而不是PHP会话。它运行良好,但我试图找出如何在AJAX请求之前刷新JWT(如果需要)。
一点背景:用户登录后,我存储JWT(10分钟到期)和刷新令牌(2小时到期)。我有以下函数,我想在每次调用API之前运行。该函数检查JWT是否过期,如果是,则通过Refresh令牌检索新的JWT。显然在API中我在允许发回数据之前检查JWT和刷新令牌的有效性。这部分工作得很好。
function checkTokenExp(){
var validToken = false; // DEFAULT TO EXPIRED
var apiClaims = localStorage.getItem('apiToken').split('.');
var api = JSON.parse(atob(apiClaims[1]));
var apiExpiration = new Date(api['exp'] * 1000);
var now = new Date();
if (apiExpiration < now) {
// API TOKEN EXPIRED - CHECK REFRESH TOKEN
var refreshClaims = localStorage.getItem('refreshToken').split('.');
var refresh = JSON.parse(atob(refreshClaims[1]));
var refreshExpiration = new Date(refresh['exp'] * 1000);
if (refreshExpiration > now) {
// REFRESH TOKEN NOT EXPIRED - NEED NEW API TOKEN
$.ajax({
type: "GET",
url: 'api/session/token',
contentType: 'application/json',
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", 'Bearer '+localStorage.getItem('refreshToken'));
}
}).done(function (data) {
var data = jQuery.parseJSON(data);
if (data.status == 'success'){
localStorage.setItem('apiToken', data.apiToken);
}
validToken = true;
});
}else{
// REFRESH TOKEN EXPIRED - FORCE LOG OUT
$("#SessionExpiredModal").modal('show');
}
}else{
// API TOKEN NOT EXPIRED
validToken = true;
}
return validToken;
}
我遇到麻烦的地方是下面的代码(以及其他所有AJAX调用)。正如你所看到的,我试图在这个AJAX调用之前运行上面的函数并验证我的JWT(如果它的无效,请求并存储新的JWT),然后用新的JWT运行AJAX调用。这一切都有效,除了AJAX返回一个未经授权的401,因为它仍在发送之前的JWT(显然是过期的)。因此,似乎在上述函数完成之前发送了AJAX并存储了新的JWT。但我知道我的功能正常运行并且JWT已更新,因为我第二次单击它正确运行的按钮,我从API得到了正确的答复。
$('#BtnTest').on('click', function(){
$.ajax({
type: "GET",
url: 'api/random',
contentType: 'application/json',
beforeSend: function (xhr) {
checkTokenExp(); // SHOULD BE SAVING NEW TOKEN
xhr.setRequestHeader("Authorization", 'Bearer '+ localStorage.getItem('apiToken')); // SHOULD BE SENDING NEW TOKEN
}
}).done(function (response) {
alert(response);
})
});
我希望这是有道理的,并提前感谢您的帮助。另外,有一种简单的方法可以在每次调用ajax之前运行我的函数,而无需在每个AJAX语句中对其进行编码。
答案 0 :(得分:0)
您需要向checkTokenExp()
添加回调,以便在API令牌刷新后完成API调用。
现在发生了什么:
api/random
beforeSend
执行checkTokenExp()
api/random
,可能是在服务器使用新令牌回复第3步之前你可以尝试类似的东西:
var currentRequests = {};
$.ajaxPrefilter(function( options, originalOptions, jqXHR ) {
// Skip URLs that don't need a token
if( options.url == 'your api/session/token URL' ) {
console.log("No ajax prefilter on api/session/token");
return;
}
successCb = function() {
currentRequests[ options.url ] = jqXHR;
};
errorCb = function() {
console.error("Could not refresh token, aborting Ajax call to " + options.url);
currentRequests[ options.url ].abort();
};
checkTokenExp(successCb, errorCb);
});
function checkTokenExp(successCb, errorCb){
// ...
if (refreshExpiration > now) {
// REFRESH TOKEN NOT EXPIRED - NEED NEW API TOKEN
$.ajax({
type: "GET",
url: 'api/session/token',
contentType: 'application/json',
}).done(function (data) {
var data = jQuery.parseJSON(data);
if (data.status == 'success'){
localStorage.setItem('apiToken', data.apiToken);
successCb(); // the new token is set, you can make ajax calls
}
// TODO: log error here, don't do validToken = true
}).fail(errorCb(data));
}else{
// REFRESH TOKEN EXPIRED - FORCE LOG OUT
$("#SessionExpiredModal").modal('show');
}
}else{
// API TOKEN NOT EXPIRED
successCb();
}
}