2017-08-31 86 views
0

请原谅我,如果这个问题完全脱离基础 - 我是JWT的新手。我有一个现有的单页应用程序,我已经转换到JWT而不是PHP会话。它的工作正常,但我想弄清楚如何刷新JWT(如有必要)在AJAX请求之前。一个小背景:在用户登录时,我存储了一个JWT(10分钟到期)和一个刷新令牌(2小时到期)。在每次AJAX调用API之前,我都想要运行以下函数。该函数检查JWT是否过期,如果是,则通过Refresh令牌检索新的JWT。显然,在API中,我会在允许数据被发回之前检查JWT和Refresh令牌的有效性。这部分工作完美。JWT刷新后运行AJAX Reqeust

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

您需要将回调添加到checkTokenExp(),以便在刷新API令牌后完成API调用。

什么是现在发生的事情:

  1. 您准备api/random
  2. 呼叫时beforeSend执行checkTokenExp()
  3. 如果的过期令牌,它要求一个又一个异步
  4. 实际调用api/random,可能在服务器回答第3步时使用新的令牌

看看jQuery's ajaxPrefilter

你可以尝试这样的事情:

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

看一看那些: https://stackoverflow.com/questions/11793430/retry-a-jquery-ajax-request-which-has - 回调 - 附加到其推迟 https://stackoverflow.com/questions/21025552/refreshing-oauth2-tokens-in-async-js https://stackoverflow.com/questions/41118347/prevent -ajax-request-until-new-token-is-obtain/41118703 – pyb

+0

感谢您的提示。它还没有工作。正如我所说,这对我来说有点新,而且我对ajaxPrefilter不熟悉。请告诉我,如果我对此的理解是正确的。 Prefilter在每个Ajax调用之前运行我的函数。 checkTokenExp()中的某处触发successCb()并允许ajax请求继续。那是对的吗。使用添加的Prefilter可以正常工作,直到令牌过期,然后它像以前一样失败。看起来好像第一个ajax调用在新的令牌保存之前仍然完成。 – bwlange3

+0

我能够用你的一个建议链接中的代码解决这个问题 - [https://stackoverflow.com/questions/41118347/prevent-ajax-request-till-new-token-is-obtained/41118703](https ://stackoverflow.com/questions/41118347/prevent-ajax-request-till-new-token-is-obtained/41118703)。我的下一个问题是这个解决方案是否安全。如果访问令牌以某种方式更改,服务器将返回401,然后通过刷新令牌请求新令牌。这是需要关注的吗? – bwlange3